fix(kernel): .rodata 映射只读 + CR0.WP=1,越界写立即 #PF 暴露写源

此前清 WP + 页表全可写,越界写 .rodata 字符串常量静默破坏(格式串 %lu 变 %lc、路径/符号名损坏连锁 → work=+/eyboard/terminal.elf 加载失败)。ProtectKernelRO 经 c.Asm 引用 __rodata_start/__rodata_end 清 PTE_WRITABLE,配合 CR0.WP 置位,越界写触发 #PF 打印 cr2/rip。
This commit is contained in:
TermiNexus committed 2026-08-29 17:34:41 +08:00
1 parent b887d3d9c2
commit 04ca2d4c5b
3 files changed
+52 -2

No files matched your search

+4 -1
View File
@@ -176,7 +176,10 @@ class ViperKernel:
mm.init(0, 0, 0, 0, 0)
pt_pool_buf2: t.CVoid | t.CPtr = mm.malloc(t.CUInt64T(513) * t.CUInt64T(4096))
paging.init_pool(pt_pool_buf2, t.CUInt64T(513) * t.CUInt64T(4096))
serial.puts("[main] paging and mm init done\n")
serial.puts("[main] paging and mm init done\n")
# 把 .rodata(字符串常量/vtable)映射只读 + CR0.WP=1:
# 越界写立即 #PF 暴露 cr2/rip(此前静默破坏格式串/路径/符号名 → P2/P3/P4/P5 连锁)
paging.ProtectKernelRO()
proc.ProcessManager()
serial.puts("[main] process manager init done\n")
+46 -1
View File
@@ -30,9 +30,12 @@ pt: _p | t.CPtr
def init(MemmapAddr: t.CUInt64T, MemmapSize: t.CUInt64T):
global pml4
pml4 = GetCurrentPml4()
# 置位 CR0.WP:内核态写只读页也会 #PF(此前清除 WP 使页表只读形同虚设,
# 越界写 .rodata 字符串常量静默破坏 → 格式串/路径/符号名损坏连锁)。
# 配合 ProtectKernelRO() 把 .rodata 页清 W 位,越界写立即 #PF 暴露写源。
c.Asm(f"""
mov rax, cr0
and rax, ~0x10000
or rax, 0x10000
mov cr0, rax
""")
@@ -183,6 +186,48 @@ def EnablePaging():
global pml4, pdp, pd, pt
c.Asm(f"mov cr3, {c.AsmInp(t.CUInt64T(pml4), t.ASM_DESCR.REG_ANY)}")
def MakeRangeRO(vaddr_start: t.CUInt64T, vaddr_end: t.CUInt64T):
"""把 [vaddr_start, vaddr_end) 范围内所有页的 PTE 清 W 位(只读)
定位越界写源:内核态写只读页在 CR0.WP=1 下触发 #PF(int=14),
isrCommonhandler 打印 cr2/rip → 写源立即暴露,不再静默破坏 .rodata。
"""
global pml4
if not pml4: return
va: t.CUInt64T = vaddr_start & ~t.CUInt64T(0xFFF)
while va < vaddr_end:
pml4_idx: t.CUInt64T = (va >> 39) & 0x1FF
if not (pml4.entries[pml4_idx] & PTE_PRESENT): va += 0x1000; continue
pdp_r: _p | t.CPtr = t.CType(pml4.entries[pml4_idx] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
pdp_idx: t.CUInt64T = (va >> 30) & 0x1FF
if not (pdp_r.entries[pdp_idx] & PTE_PRESENT): va += 0x1000; continue
pd_r: _p | t.CPtr = t.CType(pdp_r.entries[pdp_idx] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
pd_idx: t.CUInt64T = (va >> 21) & 0x1FF
if not (pd_r.entries[pd_idx] & PTE_PRESENT): va += 0x1000; continue
if pd_r.entries[pd_idx] & PTE_PS: va += 0x200000; continue
pt_r: _p | t.CPtr = t.CType(pd_r.entries[pd_idx] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
pt_idx: t.CUInt64T = (va >> 12) & 0x1FF
pt_r.entries[pt_idx] = pt_r.entries[pt_idx] & ~t.CUInt64T(PTE_WRITABLE)
c.Asm(f"invlpg [{c.AsmInp(va, t.ASM_DESCR.REG_ANY)}]")
va += 0x1000
def ProtectKernelRO():
"""把内核镜像 .rodata 段(字符串常量/vtable 等)映射为只读
地址经 c.Asm 内联引用链接器符号 __rodata_start/__rodata_end
(linker.ld 定义,TPV 不声明 extern——避免生成定义与链接器符号冲突)。
"""
global pml4
if not pml4: return
ro_start: t.CUInt64T = 0
ro_end: t.CUInt64T = 0
c.Asm(f"""lea rax, [rip + __rodata_start]
mov {c.AsmOut(ro_start, t.ASM_DESCR.OUTPUT_REG)}, rax
lea rax, [rip + __rodata_end]
mov {c.AsmOut(ro_end, t.ASM_DESCR.OUTPUT_REG)}, rax""",
op=[t.ASM_DESCR.CLOBBER_RAX])
MakeRangeRO(ro_start, ro_end)
def GetCurrentPml4() -> _p | t.CPtr:
global pml4, pdp, pd, pt
cr3: t.CUInt64T
+2
View File
@@ -10,8 +10,10 @@ SECTIONS {
}
.rodata : {
__rodata_start = .;
*(.rodata)
*(.rodata.*)
__rodata_end = .;
}
.data : {