Skip to content

[LTS 9.2] net: mdio: fix undefined behavior in bit shift for __mdiobus_register #358

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 24, 2025

Conversation

pvts-mat
Copy link
Contributor

@pvts-mat pvts-mat commented Jun 23, 2025

[LTS 9.2]
CVE-2022-49907
VULN-66416

Problem

https://www.cve.org/CVERecord?id=CVE-2022-49907

In the Linux kernel, the following vulnerability has been resolved:

net: mdio: fix undefined behavior in bit shift for __mdiobus_register

Shifting signed 32-bit value by 31 bits is undefined, so changing
significant bit to unsigned. The UBSAN warning calltrace like below:

UBSAN: shift-out-of-bounds in drivers/net/phy/mdio_bus.c:586:27
left shift of 1 by 31 places cannot be represented in type 'int'
Call Trace:
 <TASK>
 dump_stack_lvl+0x7d/0xa5
 dump_stack+0x15/0x1b
 ubsan_epilogue+0xe/0x4e
 __ubsan_handle_shift_out_of_bounds+0x1e7/0x20c
 __mdiobus_register+0x49d/0x4e0
 fixed_mdio_bus_init+0xd8/0x12d
 do_one_initcall+0x76/0x430
 kernel_init_freeable+0x3b3/0x422
 kernel_init+0x24/0x1e0
 ret_from_fork+0x1f/0x30
 </TASK>

Applicability: yes

The bug applies to LTS 9.2: the affected MDIO bus driver is central to the control of any ethernet interface device. The patch 40e4eb3 is not backported onto LTS 9.2, yet it is backported onto its mainline sibling stable 5.15 in 985a88b.

Solution

The solution in 40e4eb3 involves using the BIT(i) macro instead of the raw bit shift 1 << i to obtain an int with i -th bit set. The fully expanded BIT(i) macro boils down to 1UL << i construct operating on unsigned type where the left shit is defined for the full range of the type's bits (see include/vdso/bits.h, include/uapi/linux/const.h, include/linux/bits.h).

kABI check: passed

DEBUG=1 CVE=CVE-2022-49907 ./ninja.sh _kabi_checked__x86_64--test--ciqlts9_2-CVE-2022-49907

[0/1] Check ABI of kernel [ciqlts9_2-CVE-2022-49907]
++ uname -m
+ python3 /data/src/ctrliq-github/kernel-dist-git-el-9.2/SOURCES/check-kabi -k /data/src/ctrliq-github/kernel-dist-git-el-9.2/SOURCES/Module.kabi_x86_64 -s vms/x86_64--build--ciqlts9_2/build_files/kernel-src-tree-ciqlts9_2-CVE-2022-49907/Module.symvers
kABI check passed
+ touch state/kernels/ciqlts9_2-CVE-2022-49907/x86_64/kabi_checked

Boot test: passed

boot-test.log

Kselftests: passed relative

Coverage

All the network-related tests (except the unstable ones):

net/forwarding (except mirror_gre_bridge_1d_vlan.sh, sch_tbf_ets.sh, tc_actions.sh, sch_tbf_root.sh, vxlan_bridge_1d_ipv6.sh, sch_red.sh, dual_vxlan_bridge.sh, tc_police.sh, sch_tbf_prio.sh, mirror_gre_vlan_bridge_1q.sh, sch_ets.sh, ipip_hier_gre_keys.sh, q_in_vni.sh), net/mptcp (except simult_flows.sh, userspace_pm.sh), net (except reuseaddr_conflict, fib_nexthops.sh, reuseport_addr_any.sh, udpgro_fwd.sh, gro.sh, udpgso_bench.sh, txtimestamp.sh, xfrm_policy.sh, ip_defrag.sh), netfilter (except nft_trans_stress.sh).

Reference

kselftests–ciqlts9_2–run1.log

Patch

kselftests–ciqlts9_2-CVE-2022-49907–run1.log
kselftests–ciqlts9_2-CVE-2022-49907–run2.log

Comparison

The tests results are the same for both patch and reference

$ ktests.xsh diff -d kselftests*.log

Column    File
--------  ----------------------------------------------
Status0   kselftests--ciqlts9_2--run1.log
Status1   kselftests--ciqlts9_2-CVE-2022-49907--run1.log
Status2   kselftests--ciqlts9_2-CVE-2022-49907--run2.log

Specific tests: could not replicate

An attempt was made to replicate the bug by compiling the kernel with CONFIG_UBSAN=y. Unfortunately the integer overflows resulting from bit shifts were not being captured by UBSAN, not only in the affected driver, but in general, as could have been demonstrated with the modified test_ubsan module (enabled with CONFIG_TEST_UBSAN=y) where the test_ubsan_shift_out_of_bounds test case in lib/test_ubsan.c:

static void test_ubsan_shift_out_of_bounds(void)
{
	volatile int neg = -1, wrap = 4;
	int val1 = 10;
	int val2 = INT_MAX;

	UBSAN_TEST(CONFIG_UBSAN_SHIFT, "negative exponent");
	val1 <<= neg;

	UBSAN_TEST(CONFIG_UBSAN_SHIFT, "left overflow");
	val2 <<= wrap;
}

was augmented with the exact operation as reported in the CVE:

static void test_ubsan_shift_out_of_bounds(void)
{
	volatile int neg = -1, wrap = 4;
	int val1 = 10;
	int val2 = INT_MAX;
	int val3 = 1;
	volatile int shift = 31;

	UBSAN_TEST(CONFIG_UBSAN_SHIFT, "negative exponent");
	val1 <<= neg;

	UBSAN_TEST(CONFIG_UBSAN_SHIFT, "left overflow");
	val2 <<= wrap;

	UBSAN_TEST(CONFIG_UBSAN_SHIFT, "shift out of bounds");
	val3 <<= shift;
}

Both original "left overflow" and the added "shift out of bounds" tests were ignored by UBSAN at runtime, despite CONFIG_UBSAN_SHIFT option reported to be y:

[root@ciqlts-9-2 pvts]# modprobe test_ubsan
[   22.399968] test_ubsan_shift_out_of_bounds negative exponent (CONFIG_UBSAN_SHIFT=y)
[   22.402700] ================================================================================
[   22.405306] UBSAN: shift-out-of-bounds in /mnt/code/kernel-src-tree-ciqlts9_2-ubsan/lib/test_ubsan.c:32:7
[   22.408225] shift exponent -1 is negative
[   22.409492] CPU: 0 PID: 1566 Comm: modprobe Kdump: loaded Not tainted 5.14.0-ciqlts9_2-ubsan #1
[   22.411823] Hardware name: Red Hat KVM/RHEL, BIOS 1.16.3-2.el9_5.1 04/01/2014
[   22.413356] Call Trace:
[   22.413902]  <TASK>
[   22.414368]  dump_stack_lvl+0x34/0x48
[   22.415186]  ubsan_epilogue+0x5/0x44
[   22.415961]  __ubsan_handle_shift_out_of_bounds.cold+0x61/0xef
[   22.417206]  ? usb_hcd_alloc_bandwidth+0x111/0x400
[   22.418239]  ? 0xffffffffc02ab000
[   22.418961]  test_ubsan_shift_out_of_bounds+0x5f/0xf2 [test_ubsan]
[   22.420271]  test_ubsan_init+0x23/0x1000 [test_ubsan]
[   22.421342]  do_one_initcall+0x41/0x200
[   22.421955]  ? kmem_cache_alloc_trace+0x174/0x2f0
[   22.422677]  do_init_module+0x5c/0x260
[   22.423247]  __do_sys_finit_module+0xae/0x110
[   22.423904]  do_syscall_64+0x59/0x90
[   22.424453]  ? syscall_exit_work+0x11a/0x150
[   22.425099]  ? syscall_exit_to_user_mode+0x12/0x30
[   22.425813]  ? do_syscall_64+0x69/0x90
[   22.426376]  ? exc_page_fault+0x62/0x150
[   22.426967]  entry_SYSCALL_64_after_hwframe+0x63/0xcd
[   22.427721] RIP: 0033:0x7fb4e263ee9d
[   22.428260] Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 4b 9f 1b 00 f7 d8 64 89 01 48
[   22.430978] RSP: 002b:00007ffd8e3c0528 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[   22.431973] RAX: ffffffffffffffda RBX: 000056131792cc30 RCX: 00007fb4e263ee9d
[   22.432889] RDX: 0000000000000000 RSI: 0000561316e33962 RDI: 0000000000000003
[   22.433775] RBP: 0000000000040000 R08: 0000000000000000 R09: 00007ffd8e3c0660
[   22.434655] R10: 0000000000000003 R11: 0000000000000246 R12: 0000561316e33962
[   22.435538] R13: 000056131792cdd0 R14: 000056131792cc30 R15: 000056131792d010
[   22.436435]  </TASK>
[   22.436732] ================================================================================
[   22.437789] test_ubsan_shift_out_of_bounds left overflow (CONFIG_UBSAN_SHIFT=y)
[   22.438712] test_ubsan_shift_out_of_bounds shift out of bounds (CONFIG_UBSAN_SHIFT=y)
[   22.439688] test_ubsan_out_of_bounds above (CONFIG_UBSAN_BOUNDS=y)
[   22.440467] ================================================================================
[   22.441529] UBSAN: array-index-out-of-bounds in /mnt/code/kernel-src-tree-ciqlts9_2-ubsan/lib/test_ubsan.c:51:5
[   22.442787] index 5 is out of range for type 'int [4]'
…

After this unsuccessful replication attempt the specific testing efforts were then abandoned.

jira VULN-66416
cve CVE-2022-49907
commit-author Gaosheng Cui <[email protected]>
commit 40e4eb3

Shifting signed 32-bit value by 31 bits is undefined, so changing
significant bit to unsigned. The UBSAN warning calltrace like below:

UBSAN: shift-out-of-bounds in drivers/net/phy/mdio_bus.c:586:27
left shift of 1 by 31 places cannot be represented in type 'int'
Call Trace:
 <TASK>
 dump_stack_lvl+0x7d/0xa5
 dump_stack+0x15/0x1b
 ubsan_epilogue+0xe/0x4e
 __ubsan_handle_shift_out_of_bounds+0x1e7/0x20c
 __mdiobus_register+0x49d/0x4e0
 fixed_mdio_bus_init+0xd8/0x12d
 do_one_initcall+0x76/0x430
 kernel_init_freeable+0x3b3/0x422
 kernel_init+0x24/0x1e0
 ret_from_fork+0x1f/0x30
 </TASK>

Fixes: 4fd5f81 ("phylib: allow incremental scanning of an mii bus")
	Signed-off-by: Gaosheng Cui <[email protected]>
	Reviewed-by: Andrew Lunn <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
	Signed-off-by: Jakub Kicinski <[email protected]>
(cherry picked from commit 40e4eb3)
	Signed-off-by: Marcin Wcisło <[email protected]>
@pvts-mat pvts-mat changed the title net: mdio: fix undefined behavior in bit shift for __mdiobus_register [LTS 9.2] net: mdio: fix undefined behavior in bit shift for __mdiobus_register Jun 23, 2025
Copy link
Collaborator

@PlaidCat PlaidCat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

Copy link

@thefossguy-ciq thefossguy-ciq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚤

Copy link
Collaborator

@bmastbergen bmastbergen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥌

@PlaidCat PlaidCat merged commit 3e41a3b into ctrliq:ciqlts9_2 Jun 24, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants