fix: login cursor ghost after double save-bg + integrity GOT immunity (DSK-E70 workaround)
- login: remove duplicate cursor_save_bg after draw_login_card (ghost residue) - login: hidden visibility on __text_*/__expected_hash -> lea rip, len calc no longer depends on GOT slot - login: dsk_e70_layout_guard keeps .text out of the DSK write-zero minefield (whitelisted, see STATUS.md) - patch_login_hash: hash first PT_LOAD filesz (matches loader/runtime range) + e_shstrndx section names - STATUS.md: register DSK-E70 (unresolved ibuf-tail write-zero, root cause open)
This commit is contained in:
+24
-10
@@ -14,11 +14,14 @@
|
|||||||
|
|
||||||
#include "../UTSM/include/utsm/dsk.h"
|
#include "../UTSM/include/utsm/dsk.h"
|
||||||
|
|
||||||
/* 防篡改:linker 导出的 .text 段范围与预期哈希存储位置 */
|
/* 防篡改:linker 导出的 .text 段范围与预期哈希存储位置。
|
||||||
extern const u8 __text_start[];
|
* hidden 可见性强制 PIE 下用 lea rip 取址(基址无关),禁止走 GOT——
|
||||||
extern const u8 __text_end[];
|
* GOT slot 若被 DSK 侧后续 IO 破坏(BUG:ibuf+0x1EAE10 被清零),
|
||||||
extern const u8 __expected_hash[];
|
* len 将计算成天文数字导致 integrity 误报 LOGIN-E01。 */
|
||||||
extern const u8 __expected_hash_end[];
|
extern const u8 __text_start[] __attribute__((visibility("hidden")));
|
||||||
|
extern const u8 __text_end[] __attribute__((visibility("hidden")));
|
||||||
|
extern const u8 __expected_hash[] __attribute__((visibility("hidden")));
|
||||||
|
extern const u8 __expected_hash_end[] __attribute__((visibility("hidden")));
|
||||||
|
|
||||||
typedef signed char i8;
|
typedef signed char i8;
|
||||||
typedef unsigned char u8;
|
typedef unsigned char u8;
|
||||||
@@ -777,6 +780,20 @@ static void login_mouse_init(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((visibility("default")))
|
__attribute__((visibility("default")))
|
||||||
|
/* [DSK-E70 布局雷区规避 · 白名单登记]
|
||||||
|
* DSK 加载链路存在未定位的写零破坏:跳转 login 后 image 尾部
|
||||||
|
* (ibuf+0x1EAE10 附近,≥ 数百字节)被清零。.text ≤ ~16.5KB 时
|
||||||
|
* 雷区覆盖 .got/.bss 关键数据,导致 len 计算错误 → LOGIN-E01 误报;
|
||||||
|
* .text ≥ ~17KB 时雷区落在 .rodata/logo 数据上无害。
|
||||||
|
* 此填充把 .text 显式推入安全布局。根因修复登记 docs/STATUS.md DSK-E70,
|
||||||
|
* 修复后应删除本函数。 */
|
||||||
|
__attribute__((noinline, used)) static void dsk_e70_layout_guard(void) {
|
||||||
|
/* 1024B nop 序列,编译器不可消除 */
|
||||||
|
__asm__ volatile(
|
||||||
|
".rept 341\n\tnop\n\tnop\n\tnop\n\t.endr\n"
|
||||||
|
::: "memory");
|
||||||
|
}
|
||||||
|
|
||||||
void dsk_entry(const dsk_boot_context *ctx) {
|
void dsk_entry(const dsk_boot_context *ctx) {
|
||||||
__asm__ volatile("cli"); /* prevent IRQ1 (ps2kbd) from racing with our polling */
|
__asm__ volatile("cli"); /* prevent IRQ1 (ps2kbd) from racing with our polling */
|
||||||
logl("[login] boot");
|
logl("[login] boot");
|
||||||
@@ -862,13 +879,10 @@ void dsk_entry(const dsk_boot_context *ctx) {
|
|||||||
pass[0] = 0;
|
pass[0] = 0;
|
||||||
int plen = 0;
|
int plen = 0;
|
||||||
|
|
||||||
/* Draw initial UI */
|
/* Draw initial UI(draw_login_card 内部已含光标 save+draw,
|
||||||
|
* 此处不得重复 save——否则会把光标像素当背景存入,移动后留残影) */
|
||||||
draw_login_card(fb, card_x, card_y, pass, 1, msg, msg_color, fg);
|
draw_login_card(fb, card_x, card_y, pass, 1, msg, msg_color, fg);
|
||||||
|
|
||||||
/* 初始鼠标光标 */
|
|
||||||
cursor_save_bg(fb, g_mouse_x, g_mouse_y);
|
|
||||||
cursor_draw(fb, g_mouse_x, g_mouse_y);
|
|
||||||
|
|
||||||
/* Input loop */
|
/* Input loop */
|
||||||
int shift = 0;
|
int shift = 0;
|
||||||
int release = 0;
|
int release = 0;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ if (-not (Test-Path $ElfPath)) { Write-Error ("not found: " + $ElfPath); exit 1
|
|||||||
|
|
||||||
$bytes = [System.IO.File]::ReadAllBytes($ElfPath)
|
$bytes = [System.IO.File]::ReadAllBytes($ElfPath)
|
||||||
|
|
||||||
# ELF64 header: e_shoff @ 0x28 (u64), e_shentsize @ 0x3A (u16), e_shnum @ 0x3C (u16)
|
# ELF64 header: e_shoff @ 0x28 (u64), e_shentsize @ 0x3A (u16), e_shnum @ 0x3C (u16), e_shstrndx @ 0x3E (u16)
|
||||||
function U64([byte[]]$b, [int]$o) { [UInt64]$b[$o] -bor ([UInt64]$b[$o+1] -shl 8) -bor ([UInt64]$b[$o+2] -shl 16) -bor ([UInt64]$b[$o+3] -shl 24) -bor ([UInt64]$b[$o+4] -shl 32) -bor ([UInt64]$b[$o+5] -shl 40) -bor ([UInt64]$b[$o+6] -shl 48) -bor ([UInt64]$b[$o+7] -shl 56) }
|
function U64([byte[]]$b, [int]$o) { [UInt64]$b[$o] -bor ([UInt64]$b[$o+1] -shl 8) -bor ([UInt64]$b[$o+2] -shl 16) -bor ([UInt64]$b[$o+3] -shl 24) -bor ([UInt64]$b[$o+4] -shl 32) -bor ([UInt64]$b[$o+5] -shl 40) -bor ([UInt64]$b[$o+6] -shl 48) -bor ([UInt64]$b[$o+7] -shl 56) }
|
||||||
function U16([byte[]]$b, [int]$o) { [UInt32]$b[$o] -bor ([UInt32]$b[$o+1] -shl 8) }
|
function U16([byte[]]$b, [int]$o) { [UInt32]$b[$o] -bor ([UInt32]$b[$o+1] -shl 8) }
|
||||||
function U32([byte[]]$b, [int]$o) { [UInt32]$b[$o] -bor ([UInt32]$b[$o+1] -shl 8) -bor ([UInt32]$b[$o+2] -shl 16) -bor ([UInt32]$b[$o+3] -shl 24) }
|
function U32([byte[]]$b, [int]$o) { [UInt32]$b[$o] -bor ([UInt32]$b[$o+1] -shl 8) -bor ([UInt32]$b[$o+2] -shl 16) -bor ([UInt32]$b[$o+3] -shl 24) }
|
||||||
@@ -14,16 +14,30 @@ function U32([byte[]]$b, [int]$o) { [UInt32]$b[$o] -bor ([UInt32]$b[$o+1] -shl 8
|
|||||||
$shoff = U64 $bytes 0x28
|
$shoff = U64 $bytes 0x28
|
||||||
$shentsize = U16 $bytes 0x3A
|
$shentsize = U16 $bytes 0x3A
|
||||||
$shnum = U16 $bytes 0x3C
|
$shnum = U16 $bytes 0x3C
|
||||||
|
$shstrndx = U16 $bytes 0x3E
|
||||||
|
|
||||||
$textOff = 0; $textSize = 0; $hashOff = 0
|
$textOff = 0; $textSize = 0; $hashOff = 0
|
||||||
$shstr = $null
|
# e_shstrndx points at .shstrtab directly — picking the first SHT_STRTAB is wrong
|
||||||
for ($i = 0; $i -lt $shnum; $i++) {
|
# when .dynsym/.strtab exist (they come before .shstrtab and would garble names)
|
||||||
$sh = $shoff + $i * $shentsize
|
$shstrHdr = $shoff + $shstrndx * $shentsize
|
||||||
$shType = U32 $bytes ($sh + 4)
|
$shstrOff = U64 $bytes ($shstrHdr + 24)
|
||||||
$shOffset = U64 $bytes ($sh + 24)
|
$shstrSize = U64 $bytes ($shstrHdr + 32)
|
||||||
$shSize = U64 $bytes ($sh + 32)
|
$shstr = $bytes[[int]$shstrOff..[int]($shstrOff+$shstrSize-1)]
|
||||||
if ($shType -eq 3 -and -not $shstr) { $shstr = $bytes[[int]$shOffset..[int]($shOffset+$shSize-1)] }
|
|
||||||
|
# Hash range = first PT_LOAD segment (offset/filesz). This matches exactly what
|
||||||
|
# the DSK loader copies into memory and what the runtime hashes via
|
||||||
|
# __text_start..__text_end (the output section may include tail padding that
|
||||||
|
# the .text section header's sh_size does not cover — hashing sh_size caused
|
||||||
|
# LOGIN-E01 integrity FAIL whenever lld emitted padding after .text).
|
||||||
|
$phoff = U64 $bytes 0x20
|
||||||
|
$phentsize = U16 $bytes 0x36
|
||||||
|
$phnum = U16 $bytes 0x38
|
||||||
|
for ($i = 0; $i -lt $phnum; $i++) {
|
||||||
|
$ph = $phoff + $i * $phentsize
|
||||||
|
$pType = U32 $bytes $ph
|
||||||
|
if ($pType -eq 1) { $textOff = U64 $bytes ($ph + 8); $textSize = U64 $bytes ($ph + 32); break }
|
||||||
}
|
}
|
||||||
|
if ($textOff -eq 0) { Write-Error 'PT_LOAD text segment not found'; exit 1 }
|
||||||
|
|
||||||
function Get-SectName([byte[]]$strtab, [int]$idx) {
|
function Get-SectName([byte[]]$strtab, [int]$idx) {
|
||||||
$end = $idx
|
$end = $idx
|
||||||
@@ -38,11 +52,10 @@ for ($i = 0; $i -lt $shnum; $i++) {
|
|||||||
$shOffset = U64 $bytes ($sh + 24)
|
$shOffset = U64 $bytes ($sh + 24)
|
||||||
$shSize = U64 $bytes ($sh + 32)
|
$shSize = U64 $bytes ($sh + 32)
|
||||||
$nm = Get-SectName $shstr $nameIdx
|
$nm = Get-SectName $shstr $nameIdx
|
||||||
if ($nm -eq '.text' -and $shType -eq 1) { $textOff = $shOffset; $textSize = $shSize }
|
|
||||||
if ($nm -eq '.hashseg') { $hashOff = $shOffset }
|
if ($nm -eq '.hashseg') { $hashOff = $shOffset }
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($textOff -eq 0 -or $hashOff -eq 0) { Write-Error 'section .text/.hashseg not found'; exit 1 }
|
if ($textOff -eq 0 -or $hashOff -eq 0) { Write-Error 'PT_LOAD text/.hashseg not found'; exit 1 }
|
||||||
|
|
||||||
$sha = [System.Security.Cryptography.SHA256]::Create()
|
$sha = [System.Security.Cryptography.SHA256]::Create()
|
||||||
$textBytes = New-Object byte[] $textSize
|
$textBytes = New-Object byte[] $textSize
|
||||||
|
|||||||
@@ -88,6 +88,15 @@
|
|||||||
| 主镜像改挂 SATA(run_qemu.bat) | ✅ | DSK/AHCI 块层必须可见 GPT 盘才能访问 p2 ext4;边车退居 ide.1 | run_qemu.bat |
|
| 主镜像改挂 SATA(run_qemu.bat) | ✅ | DSK/AHCI 块层必须可见 GPT 盘才能访问 p2 ext4;边车退居 ide.1 | run_qemu.bat |
|
||||||
| 开机画面去 Logo | ✅ | 移除 Logo.png 图像,仅保留居中 "DESHAB P01" 文字;dcp/xj logo 改由 TEXTURES/ 重新生成 | build 日志 |
|
| 开机画面去 Logo | ✅ | 移除 Logo.png 图像,仅保留居中 "DESHAB P01" 文字;dcp/xj logo 改由 TEXTURES/ 重新生成 | build 日志 |
|
||||||
|
|
||||||
|
## DSK-E70:ibuf 尾部写零破坏(2026-10-05 · 未解)
|
||||||
|
|
||||||
|
| 项 | 状态 | 说明 | 证据 |
|
||||||
|
|----|------|------|------|
|
||||||
|
| 现象 | 🔶 | DSK 经 `dsk_load_elf` 载入 login.elf 并跳转后,image 尾部(ibuf+0x1EAE10 附近,≥ 数百字节)被清零。`.text` ≤ ~16.5KB 时雷区覆盖 .got/.bss 关键数据 → login integrity 校验 len 计算错误 → LOGIN-E01 误报;`.text` ≥ ~17KB 时雷区落在 .rodata 无害 | `.build_tmp/qemu_dbg.log`(boot 矩阵:16248B FAIL×4 / 16504B FAIL×2 / 17352、17608B OK;DSK 侧 checksum 跳转前位精确 + QMP 物理内存 dump 停机后 .text 位精确与 got[0]=0 并存) |
|
||||||
|
| login 侧加固 | ✅ | ① `__text_start/end/__expected_hash` 加 hidden 可见性 → PIE 下 lea rip 取址,len 计算零 GOT 依赖;② `dsk_e70_layout_guard` 显式将 .text 推入安全布局(白名单登记,根因修复后删除) | CODE/login/main.c |
|
||||||
|
| patch 侧加固 | ✅ | patch_login_hash.ps1 改按 PT_LOAD filesz 计算 hash 范围(与 DSK loader 加载范围/运行时 [__text_start,__text_end) 严格一致),并用 e_shstrndx 定位 .shstrtab | CODE/login/patch_login_hash.ps1 |
|
||||||
|
| 根因(写零者) | ⬜ | 嫌疑面:AHCI DMA PRDT 边界 / fat32_read_sectors 缓冲边界 / DSK .bss 内大缓冲(g_fdata 10MB、ibuf 8MB)相邻越界。写零发生在跳转后、sha256 前;确切写者与范围待断点级排查 | — |
|
||||||
|
|
||||||
## 文档体系
|
## 文档体系
|
||||||
|
|
||||||
| 文档 | 状态 | 说明 |
|
| 文档 | 状态 | 说明 |
|
||||||
|
|||||||
Reference in New Issue
Block a user