Skip to content

[LTS 9.2] udmabuf: fix a buf size overflow issue during udmabuf creation #389

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
Jul 7, 2025

Conversation

pvts-mat
Copy link
Contributor

@pvts-mat pvts-mat commented Jul 1, 2025

[LTS 9.2]
CVE-2025-37803
VULN-67673

Problem

https://nvd.nist.gov/vuln/detail/CVE-2025-37803

udmabuf: fix a buf size overflow issue during udmabuf creation

by casting size_limit_mb to u64  when calculate pglimit.

Background

The problem applies to the udmabuf module which provides a mechanism for user-space applications to allocate and manage DMA (Direct Memory Access) buffers. The module has two parameters adjustable from the user space:

$ ls -lh /sys/module/udmabuf/parameters/

-rw-r--r--. 1 root root 4.0K Jul  1 19:24 list_limit
-rw-r--r--. 1 root root 4.0K Jul  1 19:24 size_limit_mb

Their meaning:

# modinfo udmabuf

name:           udmabuf
filename:       (builtin)
license:        GPL v2
file:           drivers/dma-buf/udmabuf
author:         Gerd Hoffmann <[email protected]>
parm:           list_limit:udmabuf_create_list->count limit. Default is 1024. (int)
parm:           size_limit_mb:Max size of a dmabuf, in megabytes. Default is 64. (int)

The overflow mentioned in 021ba7f's message occurs during the conversion of size_limit_mb into the number of pages upon the allocation of a new DMA buffer.

drivers/dma-buf/udmabuf.c:

pglimit = (size_limit_mb * 1024 * 1024) >> PAGE_SHIFT;

Applicability: yes

The udmabuf module is enabled with y in all configuration variants for LTS 9.2:

$ grep CONFIG_UDMABUF configs/kernel*.config

configs/kernel-aarch64-64k-debug-rhel.config:CONFIG_UDMABUF=y
configs/kernel-aarch64-64k-rhel.config:CONFIG_UDMABUF=y
configs/kernel-aarch64-debug-rhel.config:CONFIG_UDMABUF=y
configs/kernel-aarch64-rhel.config:CONFIG_UDMABUF=y
configs/kernel-ppc64le-debug-rhel.config:CONFIG_UDMABUF=y
configs/kernel-ppc64le-rhel.config:CONFIG_UDMABUF=y
configs/kernel-s390x-debug-rhel.config:CONFIG_UDMABUF=y
configs/kernel-s390x-rhel.config:CONFIG_UDMABUF=y
configs/kernel-s390x-zfcpdump-rhel.config:CONFIG_UDMABUF=y
configs/kernel-x86_64-debug-rhel.config:CONFIG_UDMABUF=y
configs/kernel-x86_64-rhel.config:CONFIG_UDMABUF=y

The conversion found in

pglimit = (size_limit_mb * 1024 * 1024) >> PAGE_SHIFT;
doesn't contain the u64 cast mentioned in the CVE.

Solution

Mainline fix contained in 021ba7f. Applies to ciqlts9_2 without any issues.

kABI check: passed

DEBUG=1 CVE=CVE-2025-37803 ./ninja.sh _kabi_checked__x86_64--test--ciqlts9_2-CVE-2025-37803

ninja: Entering directory `/data/build/rocky-patching'
[0/1] Check ABI of kernel [ciqlts9_2-CVE-2025-37803]
++ 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-2025-37803/Module.symvers
kABI check passed
+ touch state/kernels/ciqlts9_2-CVE-2025-37803/x86_64/kabi_checked

Boot test: passed

boot-test.log

Kselftests

Module: passed

A selftest exists in LTS 9.2 testing the udmabuf module directly: drivers/dma-buf:udmabuf. It was run several times on a reference and patched kernel

Reference

kselftest-udmabuf–ciqlts9_2–run1.log
kselftest-udmabuf–ciqlts9_2–run2.log
kselftest-udmabuf–ciqlts9_2–run3.log
kselftest-udmabuf–ciqlts9_2–run4.log
kselftest-udmabuf–ciqlts9_2–run5.log
kselftest-udmabuf–ciqlts9_2–run6.log

Patch

kselftest-udmabuf–ciqlts9_2-CVE-2025-37803–run1.log
kselftest-udmabuf–ciqlts9_2-CVE-2025-37803–run2.log
kselftest-udmabuf–ciqlts9_2-CVE-2025-37803–run3.log
kselftest-udmabuf–ciqlts9_2-CVE-2025-37803–run4.log
kselftest-udmabuf–ciqlts9_2-CVE-2025-37803–run5.log
kselftest-udmabuf–ciqlts9_2-CVE-2025-37803–run6.log

Comparison

The test passes consistently in both reference and patched kernel.

$ ktests.xsh diff  kselftest-udmabuf*.log

Column    File
--------  -----------------------------------------------------
Status0   kselftest-udmabuf--ciqlts9_2--run1.log
Status1   kselftest-udmabuf--ciqlts9_2--run2.log
Status2   kselftest-udmabuf--ciqlts9_2--run3.log
Status3   kselftest-udmabuf--ciqlts9_2--run4.log
Status4   kselftest-udmabuf--ciqlts9_2--run5.log
Status5   kselftest-udmabuf--ciqlts9_2--run6.log
Status6   kselftest-udmabuf--ciqlts9_2-CVE-2025-37803--run1.log
Status7   kselftest-udmabuf--ciqlts9_2-CVE-2025-37803--run2.log
Status8   kselftest-udmabuf--ciqlts9_2-CVE-2025-37803--run3.log
Status9   kselftest-udmabuf--ciqlts9_2-CVE-2025-37803--run4.log
Status10  kselftest-udmabuf--ciqlts9_2-CVE-2025-37803--run5.log
Status11  kselftest-udmabuf--ciqlts9_2-CVE-2025-37803--run6.log

TestCase                 Status0  Status1  Status2  Status3  Status4  Status5  Status6  Status7  Status8  Status9  Status10  Status11  Summary
drivers/dma-buf:udmabuf  pass     pass     pass     pass     pass     pass     pass     pass     pass     pass     pass      pass      same

General: passed relative

Coverage

All available tests except unstable ones.

bpf (except test_progs, test_kmod.sh, test_sockmap, test_progs-no_alu32, test_xsk.sh), breakpoints (except step_after_suspend_test), capabilities, cgroup (except test_freezer, test_memcontrol), clone3, core, cpu-hotplug, cpufreq, drivers/dma-buf, drivers/net/bonding, drivers/net/team, filesystems/binderfs, firmware, fpu, ftrace, futex, gpio, intel_pstate, ipc, ir, kcmp, kexec, kvm, landlock, lib, livepatch, membarrier, memfd, memory-hotplug, mincore, mount, mqueue, nci, net/forwarding (except mirror_gre_vlan_bridge_1q.sh, tc_actions.sh, q_in_vni.sh, sch_tbf_root.sh, mirror_gre_bridge_1d_vlan.sh, dual_vxlan_bridge.sh, sch_tbf_prio.sh, vxlan_bridge_1d_ipv6.sh, sch_ets.sh, gre_inner_v6_multipath.sh, tc_police.sh, sch_red.sh, sch_tbf_ets.sh, ipip_hier_gre_keys.sh), net/mptcp (except userspace_pm.sh, simult_flows.sh, mptcp_join.sh), net (except txtimestamp.sh, xfrm_policy.sh, udpgso_bench.sh, udpgro_fwd.sh, gro.sh, reuseport_addr_any.sh, ip_defrag.sh, fib_nexthops.sh, reuseaddr_conflict), netfilter (except nft_trans_stress.sh), nsfs, openat2, pid_namespace, pidfd, proc (except proc-uptime-001, proc-pid-vm), pstore, ptrace, rlimits, rseq, seccomp, sgx, sigaltstack, size, splice, static_keys, syscall_user_dispatch, tc-testing, tdx, timens, timers (except raw_skew), tmpfs, tpm2, vDSO, vm, x86, zram

Reference

kselftests–ciqlts9_2–run1.log

Patch

kselftests–ciqlts9_2-CVE-2025-37803–run1.log

Comparison

The test results in reference and patch are the same

$ ktests.xsh diff -d  kselftests--*.log

Column    File
--------  ----------------------------------------------
Status0   kselftests--ciqlts9_2--run1.log
Status1   kselftests--ciqlts9_2-CVE-2025-37803--run1.log

Commentary and the specific tests

Commentary

The bug is not a vulnerability, or at least not in the sense that's implied in the CVE, which is classified by NIST as "CWE-120 Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')".

The pglimit variable during the calculation of which the overflow may occur is local to the udmabuf_create function and is used in exactly two places

  1. Value assignment
    pglimit = (size_limit_mb * 1024 * 1024) >> PAGE_SHIFT;
  2. Value read
    if (ubuf->pagecount > pglimit)
    goto err;

The variable serves as a limit on the DMA buffer's number of pages, imposed by the system administrator, like

# echo 128 > /sys/module/udmabuf/parameters/size_limit_mb

For the system with the PAGE_SIZE of 4096 (PAGE_SHIFT = 12), this translates through

pglimit = (size_limit_mb * 1024 * 1024) >> PAGE_SHIFT;

to pglimit = 128 * 1024 * 1024 / 4096 = 32768 pages.

The overflow in the calculation of pglimit is indistinguishable from the case of the administrator setting the "overflown" value directly. Assuming the module handles the limiting logic correctly the bug therefore cannot have any influence on the module's memory safety - there is no buffer overflow occuring. (If this assumption is wrong then the module has a bigger problem which the 021ba7f commit doesn't fix anyway).

Moreover, the overflow, if occurs, always results in the pglimit value being no greater than intended. This can be proved by considering the only two possible scenarios for the overflow of size_limit_mb * 1024 * 1024 expression:

  • The most significant bit of size_limit_mb * 1024 * 1024 is 0: The "classic" overflow of a positive integer x resulting in a truncated value x' = x mod 2ᴺ, further reduced by dividing by PAGE_SIZE (for N = number of bits in int)
  • The most significant bit of size_limit_mb * 1024 * 1024 is 1: the "negative" overflow, with the original positive integer x becoming x' - 2ᴺ < 0, where x' = x mod 2ᴺ. Right-shifting such value increases it, but keeps it negative, by the virtue of right shift preserving the most significant bit of the int.

Bug reproduction

Bug reproduction is especially easy given that the selftest drivers/dma-buf:udmabuf (https://github.com/ctrliq/kernel-src-tree/blob/ciqlts9_2/tools/testing/selftests/drivers/dma-buf/udmabuf.c) is alsso a nice example of the basic usage of the udmabuf module, so there is no need to write any specific testing POCs. The drivers/dma-buf:udmabuf test tries to allocate a DMA buffer of the size of 4 pages

Using size_limit_mb = 1:

[root@ciqlts-9-2 pvts]# echo 1 > /sys/module/udmabuf/parameters/size_limit_mb

which translates on the testing machine to 256 pages, the test works fine, since 4 < 256:

[root@ciqlts-9-2 pvts]# …/tools/testing/selftests/drivers/dma-buf/udmabuf
drivers/dma-buf/udmabuf: ok

After setting the MB limit to 0 the test fails:

[root@ciqlts-9-2 pvts]# echo 0 > /sys/module/udmabuf/parameters/size_limit_mb
[root@ciqlts-9-2 pvts]# …/tools/testing/selftests/drivers/dma-buf/udmabuf
drivers/dma-buf/udmabuf: [FAIL,test-4]

at the ioctl call where the limit is checked:

/* should work */
create.memfd = memfd;
create.offset = 0;
create.size = size;
buf = ioctl(devfd, UDMABUF_CREATE, &create);
if (buf < 0) {
printf("%s: [FAIL,test-4]\n", TEST_PREFIX);
exit(1);
}

An int overflow resulting in pglimit = 0 can be achieved with size_limit_mb * 1024 * 1024 = 2³², for the int size of 32, which solved for size_limit_mb gives size_limit_mb = 4096:

[root@ciqlts-9-2 pvts]# echo 4096 > /sys/module/udmabuf/parameters/size_limit_mb
[root@ciqlts-9-2 pvts]# …/tools/testing/selftests/drivers/dma-buf/udmabuf
drivers/dma-buf/udmabuf: [FAIL,test-4]

Setting it to 4096 + 1 makes the test working again

[root@ciqlts-9-2 pvts]# echo 4097 > /sys/module/udmabuf/parameters/size_limit_mb
[root@ciqlts-9-2 pvts]# tools/testing/selftests/drivers/dma-buf/udmabuf
drivers/dma-buf/udmabuf: ok

This is equivalent to setting size_limit_mb to 1 as in the beginning.

Specific patch test: passed

Repeating the condition for pglimit overflow on the patched kernel results in the selftest working fine:

[root@ciqlts-9-2 pvts]# echo 4096 > /sys/module/udmabuf/parameters/size_limit_mb
[root@ciqlts-9-2 pvts]# …/tools/testing/selftests/drivers/dma-buf/udmabuf
drivers/dma-buf/udmabuf: ok

jira VULN-67673
cve CVE-2025-37803
commit-author Xiaogang Chen <[email protected]>
commit 021ba7f

by casting size_limit_mb to u64  when calculate pglimit.

	Signed-off-by: Xiaogang Chen<[email protected]>
Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
	Signed-off-by: Christian König <[email protected]>
(cherry picked from commit 021ba7f)
	Signed-off-by: Marcin Wcisło <[email protected]>
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
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.

🥌

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.

🚤

@PlaidCat PlaidCat merged commit f539a2b into ctrliq:ciqlts9_2 Jul 7, 2025
2 of 3 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