fix(mm,explorer): slab 页严格归属与分配位图;关闭按钮按下态与松手关闭
① [mm] BAD blk 的解码:site=2=free_block、reason=1=magic 失配;blk 实际是 slab_page+16(页在 0x1b56020),sz 是该页 free_head,而非块大小。被踩的是页头前 8~16 字节,最可能是无锁 buddy 在 usb.init(sched.enable 之后)与用户进程 MMAP 并发 malloc 时重复授予同一 8KB 块。② mm 加固:_slab_find_page 不再只看 magic,新增 _slab_page_owns(block_ptr 回指、指针在对象区内、按 obj_size 对齐、序号 < total_objs);slab_page 新增 alloc_bits,只有已分配对象可 free,重复释放/野指针被拒,避免 free_count 提前到 total_objs 把仍在用的页拆回 buddy;free_obj 拒绝已全空继续收对象;_create_page 失败路径清零 magic。③ hid_ascii 仅只读且已有边界检查,非本次堆损坏来源;usb_kbd 把 code >= 64 改为显式早返回。④ 关闭按钮:按下只置 close_down 并置脏(按钮画按下态:CLASSIC 40,40,70→20,20,45;WIN7/XP 红底 200,50,50→140,25,25;WIN10/11 补深灰底),松手且仍命中才发 CLOSE + used=0,移出则撤销按下态恢复;几何复用 _close_btn_* 同一套函数。
This commit is contained in:
1 parent
bd1cd2f228
commit
7e7204a09a
3 files changed
+146
-40
No files matched your search
@@ -144,8 +144,9 @@ class DeskServer:
|
||||
pcy: int
|
||||
focused: int
|
||||
next_z: int
|
||||
btn_down: int
|
||||
drag_win: int
|
||||
btn_down: int
|
||||
close_down: int # 正在被按下的关闭键所属窗口(-1 = 无)
|
||||
drag_win: int
|
||||
drag_dx: int
|
||||
drag_dy: int
|
||||
# 复用缓冲
|
||||
@@ -189,6 +190,7 @@ class DeskServer:
|
||||
self.focused = -1
|
||||
self.next_z = 1
|
||||
self.btn_down = 0
|
||||
self.close_down = -1
|
||||
self.drag_win = -1
|
||||
self.drag_dx = 0
|
||||
self.drag_dy = 0
|
||||
@@ -679,7 +681,7 @@ class DeskServer:
|
||||
if hi > lo:
|
||||
self.row_fill(bx + lo, yy, hi - lo, col)
|
||||
|
||||
def _draw_close_row(self, bx: int, by: int, bw: int, bh: int, yy: int, style: int):
|
||||
def _draw_close_row(self, bx: int, by: int, bw: int, bh: int, yy: int, style: int, pressed: int):
|
||||
lry: int = yy - by
|
||||
if lry < 0 or lry >= bh: return
|
||||
cx: int = bx + bw / 2
|
||||
@@ -688,12 +690,19 @@ class DeskServer:
|
||||
if bw >= 30: d = 4
|
||||
x_col: uint32 = self.rgb(uint32(200), uint32(200), uint32(200))
|
||||
if style == STYLE_CLASSIC:
|
||||
self.row_fill(bx, yy, bw, self.rgb(uint32(40), uint32(40), uint32(70)))
|
||||
base_col: uint32 = self.rgb(uint32(40), uint32(40), uint32(70))
|
||||
if pressed != 0: base_col = self.rgb(uint32(20), uint32(20), uint32(45))
|
||||
self.row_fill(bx, yy, bw, base_col)
|
||||
elif style == STYLE_WIN7 or style == STYLE_WINXP:
|
||||
self._round_fill_row(bx, by, bw, bh, yy, self.rgb(uint32(200), uint32(50), uint32(50)), 3)
|
||||
base_col2: uint32 = self.rgb(uint32(200), uint32(50), uint32(50))
|
||||
if pressed != 0: base_col2 = self.rgb(uint32(140), uint32(25), uint32(25))
|
||||
self._round_fill_row(bx, by, bw, bh, yy, base_col2, 3)
|
||||
x_col = self.rgb(uint32(255), uint32(255), uint32(255))
|
||||
else:
|
||||
x_col = self.rgb(uint32(150), uint32(150), uint32(150))
|
||||
# WIN10/WIN11 平时无底色,按下时补一块深灰底做按压反馈
|
||||
if pressed != 0:
|
||||
self.row_fill(bx, yy, bw, self.rgb(uint32(70), uint32(70), uint32(70)))
|
||||
i: int = yy - cy
|
||||
if i >= 0 - d and i <= d:
|
||||
self.row_fill(cx + i, yy, 1, x_col)
|
||||
@@ -739,7 +748,17 @@ class DeskServer:
|
||||
if style == STYLE_WIN7 or style == STYLE_WINXP: return 5
|
||||
return 4
|
||||
|
||||
def _draw_window_btns(self, s: desk.WinSlot*, y0: int, y1: int, th: int):
|
||||
# 关闭键命中:与 _draw_window_btns 共用同一套几何
|
||||
def _close_btn_hit(self, s: desk.WinSlot*, x: int, y: int) -> int:
|
||||
cw: int = self._close_btn_w(s.style)
|
||||
ch: int = self._close_btn_h(s.style)
|
||||
bx: int = s.ux + s.w - cw - self._close_btn_pad(s.style)
|
||||
by: int = s.uy + (TITLE_H - ch) / 2
|
||||
if x >= bx and x < bx + cw and y >= by and y < by + ch:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def _draw_window_btns(self, s: desk.WinSlot*, y0: int, y1: int, th: int, close_pressed: int):
|
||||
wx: int = s.ux
|
||||
wy: int = s.uy
|
||||
ww: int = s.w
|
||||
@@ -757,7 +776,7 @@ class DeskServer:
|
||||
yend: int = y1
|
||||
if yend > by + bh: yend = by + bh
|
||||
while yy < yend:
|
||||
self._draw_close_row(close_x, by, bw, bh, yy, style)
|
||||
self._draw_close_row(close_x, by, bw, bh, yy, style, close_pressed)
|
||||
self._draw_min_row(min_x, by, bw, bh, yy, style)
|
||||
self._draw_max_row(max_x, by, bw, bh, yy, style)
|
||||
yy += 1
|
||||
@@ -1038,7 +1057,9 @@ class DeskServer:
|
||||
title_alpha, buf, fw, fh, content_y0)
|
||||
yy += 1
|
||||
|
||||
self._draw_window_btns(s, y0, y1, th)
|
||||
close_pressed: int = 0
|
||||
if self.close_down == win: close_pressed = 1
|
||||
self._draw_window_btns(s, y0, y1, th, close_pressed)
|
||||
if s.title[0] != 0:
|
||||
text_y: int = wy + (th - vpui.CHAR_H) / 2
|
||||
if text_y + vpui.CHAR_H > y0 and text_y < y1:
|
||||
@@ -1157,6 +1178,7 @@ class DeskServer:
|
||||
if self.focused == win: self.focused = -1
|
||||
# 拖拽中的窗口被销毁:必须清掉 drag_win,否则后续移动会继续操作这个死槽位。
|
||||
if self.drag_win == win: self.drag_win = -1
|
||||
if self.close_down == win: self.close_down = -1
|
||||
self.mark_all_windows()
|
||||
self.mark_titles()
|
||||
elif ty == uint32(desk.DESK_REQ_MOVE):
|
||||
@@ -1287,6 +1309,12 @@ class DeskServer:
|
||||
self.mark(self.cy - 1, CURSOR_BMP_H + 2)
|
||||
self.pcx = self.cx
|
||||
self.pcy = self.cy
|
||||
# 按住关闭键时移出按钮:撤销按下态并置脏恢复
|
||||
if self.close_down >= 0:
|
||||
cm: desk.WinSlot* = self.slot(self.close_down)
|
||||
if cm.used == 0 or self._close_btn_hit(cm, self.cx, self.cy) == 0:
|
||||
if cm.used != 0: self.mark(cm.uy, TITLE_H)
|
||||
self.close_down = -1
|
||||
# 悬停:把 HOVER 投给光标下那个窗口(窗口内坐标)
|
||||
if self.btn_down == 0:
|
||||
hw: int = self.hit_test(self.cx, self.cy)
|
||||
@@ -1297,6 +1325,7 @@ class DeskServer:
|
||||
self.cx - hs.ux, self.cy - hs.uy - TITLE_H - TITLE_GAP, 0)
|
||||
if saw_press != 0:
|
||||
self.btn_down = 1
|
||||
self.close_down = -1 # 新手势开始:清掉上一次可能丢失释放沿留下的按下态
|
||||
# 任务栏图标优先:命中则激活并跳过窗口命中/拖动
|
||||
hit: int = -1
|
||||
tw: int = self._taskbar_icon_hit(self.cx, self.cy)
|
||||
@@ -1306,25 +1335,13 @@ class DeskServer:
|
||||
hit = self.hit_test(self.cx, self.cy)
|
||||
if hit >= 0:
|
||||
s: desk.WinSlot* = self.slot(hit)
|
||||
# 关闭键热区按样式尺寸算,必须与 _draw_window_btns 一致
|
||||
cw: int = self._close_btn_w(s.style)
|
||||
ch: int = self._close_btn_h(s.style)
|
||||
bx: int = s.ux + s.w - cw - self._close_btn_pad(s.style)
|
||||
by: int = s.uy + (TITLE_H - ch) / 2
|
||||
in_close: int = 0
|
||||
if self.cx >= bx and self.cx < bx + cw and self.cy >= by and self.cy < by + ch:
|
||||
in_close = 1
|
||||
# 关闭键热区复用 _close_btn_hit(与 _draw_window_btns 同一套几何)
|
||||
in_close: int = self._close_btn_hit(s, self.cx, self.cy)
|
||||
self.focus(hit)
|
||||
if in_close != 0:
|
||||
# 关闭:通知一次 CLOSE 后把槽位置空回收;window.vp 的 is_active()
|
||||
# 见到 used==0 即返回 0,app 的 while 循环自然退出。
|
||||
# 像素缓冲不回收(arena 是 bump 分配),只放槽位。
|
||||
self.push_event(hit, uint32(desk.DESK_EVT_CLOSE), 0, 0, 0, 0)
|
||||
self.mark_win(s.uy, s.h)
|
||||
s.used = 0
|
||||
if self.focused == hit: self.focused = -1
|
||||
if self.drag_win == hit: self.drag_win = -1
|
||||
self.mark_titles()
|
||||
# 只进入按下态:松手时才真正关窗,保证按下反馈至少能刷出一帧。
|
||||
self.close_down = hit
|
||||
self.mark(s.uy, TITLE_H)
|
||||
elif self.cy < s.uy + TITLE_H:
|
||||
# 标题栏(非关闭按钮)⇒ 开始拖动
|
||||
self.drag_win = hit
|
||||
@@ -1339,7 +1356,24 @@ class DeskServer:
|
||||
# 否则 btn_down 卡在 1,后续点击永远检测不到按下沿。
|
||||
if cur == 0 and self.btn_down != 0:
|
||||
self.btn_down = 0
|
||||
if self.drag_win < 0:
|
||||
cw_win: int = self.close_down
|
||||
self.close_down = -1
|
||||
if cw_win >= 0:
|
||||
cs: desk.WinSlot* = self.slot(cw_win)
|
||||
if cs.used != 0:
|
||||
self.mark(cs.uy, TITLE_H)
|
||||
if self._close_btn_hit(cs, self.cx, self.cy) != 0:
|
||||
# 关闭:通知一次 CLOSE 后把槽位置空回收;window.vp 的 is_active()
|
||||
# 见到 used==0 即返回 0,app 的 while 循环自然退出。
|
||||
# 像素缓冲不回收(arena 是 bump 分配),只放槽位。
|
||||
self.push_event(cw_win, uint32(desk.DESK_EVT_CLOSE), 0, 0, 0, 0)
|
||||
self.mark_win(cs.uy, cs.h)
|
||||
cs.used = 0
|
||||
if self.focused == cw_win: self.focused = -1
|
||||
if self.drag_win == cw_win: self.drag_win = -1
|
||||
self.mark_all_windows()
|
||||
self.mark_titles()
|
||||
elif self.drag_win < 0:
|
||||
# 松手:投 RELEASE 给光标下的窗口
|
||||
rw: int = self.hit_test(self.cx, self.cy)
|
||||
if rw >= 0:
|
||||
|
||||
@@ -63,15 +63,16 @@ def _decode_press(code: uint8, modifier: uint8):
|
||||
elif code == uint8(0x4C): keyboard.inject_csi_tilde(uint8(0x33)) # Delete
|
||||
elif code == uint8(0x4B): keyboard.inject_csi_tilde(uint8(0x35)) # PageUp
|
||||
elif code == uint8(0x4E): keyboard.inject_csi_tilde(uint8(0x36)) # PageDown
|
||||
elif int(code) < 64:
|
||||
shifted: bool = (modifier & uint8(0x22)) != 0
|
||||
if kbd_caps != 0 and code >= uint8(0x04) and code <= uint8(0x1D):
|
||||
shifted = not shifted
|
||||
ch: char = hid_ascii[int(code)]
|
||||
if shifted:
|
||||
ch = hid_ascii_shift[int(code)]
|
||||
if ch != '\0':
|
||||
keyboard.inject_byte(uint8(ch))
|
||||
# usage ID 最大 0xE7;ASCII 表只有 64 项,越界前必须先拒绝(表是只读的,但读越界同样非法)。
|
||||
if int(code) >= 64: return
|
||||
shifted: bool = (modifier & uint8(0x22)) != 0
|
||||
if kbd_caps != 0 and code >= uint8(0x04) and code <= uint8(0x1D):
|
||||
shifted = not shifted
|
||||
ch: char = hid_ascii[int(code)]
|
||||
if shifted:
|
||||
ch = hid_ascii_shift[int(code)]
|
||||
if ch != '\0':
|
||||
keyboard.inject_byte(uint8(ch))
|
||||
|
||||
def init() -> int:
|
||||
global kbd_dev, kbd_initialized, kbd_slot_id
|
||||
|
||||
+76
-5
@@ -66,9 +66,10 @@ class __buddy_system:
|
||||
end_addr: rawptr # 内存结束地址
|
||||
|
||||
|
||||
define SLAB_MAGIC = 0x534C4142
|
||||
define SLAB_NUM_SIZES = 6
|
||||
define SLAB_THRESHOLD = 1024
|
||||
define SLAB_MAGIC = 0x534C4142
|
||||
define SLAB_NUM_SIZES = 6
|
||||
define SLAB_THRESHOLD = 1024
|
||||
define SLAB_BITMAP_BYTES = 32
|
||||
|
||||
class slab_page:
|
||||
magic: uint32
|
||||
@@ -80,6 +81,10 @@ class slab_page:
|
||||
block_ptr: memory_block*
|
||||
next: slab_page*
|
||||
prev: slab_page*
|
||||
# 每对象 1 bit:只有置位的对象才能被 free。释放未分配对象(重复释放/野指针撞上
|
||||
# 页内空闲对象)会让 free_count 提前到达 total_objs ⇒ 页被拆回 buddy,而页内仍
|
||||
# 有活对象,随后它的 free 就只剩 magic=0 的旧页头(现场 0x1b56030 即 page+16)。
|
||||
alloc_bits: [SLAB_BITMAP_BYTES]uint8
|
||||
|
||||
|
||||
# 4K对齐函数
|
||||
@@ -326,18 +331,62 @@ def _slab_write_ptr(addr: rawptr, val: rawptr):
|
||||
mov [{asminp(addr, ASM_DESCR.REG_ANY)}], rax""",
|
||||
op=[ASM_DESCR.CLOBBER_RAX, ASM_DESCR.CLOBBER_MEMORY])
|
||||
|
||||
# 对象在页内的序号(-1 = 不在对象边界上)
|
||||
def _slab_bit_index(page: slab_page*, ptr: rawptr) -> int:
|
||||
if page.obj_size == 0: return -1
|
||||
hdr_off: uint64 = memory_block.__sizeof__()
|
||||
block: uint64 = uint64(page) - hdr_off
|
||||
data: uint64 = block + uint64(page.data_offset)
|
||||
if uint64(ptr) < data: return -1
|
||||
return int((uint64(ptr) - data) / uint64(page.obj_size))
|
||||
|
||||
def _slab_bit_set(page: slab_page*, idx: int):
|
||||
if idx < 0 or idx >= SLAB_BITMAP_BYTES * 8: return
|
||||
byte_idx: int = idx / 8
|
||||
page.alloc_bits[byte_idx] = uint8(page.alloc_bits[byte_idx] | uint8(1 << (idx & 7)))
|
||||
|
||||
def _slab_bit_clear(page: slab_page*, idx: int):
|
||||
if idx < 0 or idx >= SLAB_BITMAP_BYTES * 8: return
|
||||
byte_idx: int = idx / 8
|
||||
page.alloc_bits[byte_idx] = uint8(page.alloc_bits[byte_idx] & ~uint8(1 << (idx & 7)))
|
||||
|
||||
def _slab_bit_test(page: slab_page*, idx: int) -> int:
|
||||
if idx < 0 or idx >= SLAB_BITMAP_BYTES * 8: return 0
|
||||
byte_idx: int = idx / 8
|
||||
mask: uint8 = uint8(1 << (idx & 7))
|
||||
if (page.alloc_bits[byte_idx] & mask) != 0: return 1
|
||||
return 0
|
||||
|
||||
# slab 页归属校验:页头必须自洽(block_ptr 指回本页所在块),且 ptr 必须落在页内
|
||||
# [data_offset, data_offset + total*obj) 范围并按其对象大小对齐。只查 magic 会把
|
||||
# 大块分配头部残留的“看起来像页头”的数据误判为 slab 页,free 会写进页外并把仍在
|
||||
# 使用的块还给 buddy(现场:页头 magic 被清零后 free(err) 落到 buddy 路径)。
|
||||
def _slab_page_owns(page: slab_page*, ptr: rawptr, hdr_off: uint64) -> int:
|
||||
if page.magic != SLAB_MAGIC: return 0
|
||||
obj: uint64 = uint64(page.obj_size)
|
||||
if obj == 0: return 0
|
||||
block: uint64 = uint64(page) - hdr_off
|
||||
if uint64(page.block_ptr) != block: return 0
|
||||
if uint64(page.data_offset) < hdr_off: return 0
|
||||
data: uint64 = block + uint64(page.data_offset)
|
||||
if uint64(ptr) < data: return 0
|
||||
off: uint64 = uint64(ptr) - data
|
||||
if (off % obj) != 0: return 0
|
||||
if (off / obj) >= uint64(page.total_objs): return 0
|
||||
return 1
|
||||
|
||||
def _slab_find_page(ptr: rawptr) -> slab_page*:
|
||||
if ptr is None: return None
|
||||
hdr_off: uint64 = memory_block.__sizeof__()
|
||||
page_addr: uint64 = uint64(ptr) & ~uint64(0xFFF)
|
||||
page: slab_page* = slab_page*(page_addr + hdr_off)
|
||||
if page.magic == SLAB_MAGIC:
|
||||
if _slab_page_owns(page, ptr, hdr_off) != 0:
|
||||
os: uint16 = page.obj_size
|
||||
if os == 32 or os == 64 or os == 128 or os == 256 or os == 512 or os == 1024:
|
||||
return page
|
||||
page_addr2: uint64 = page_addr - 4096
|
||||
page2: slab_page* = slab_page*(page_addr2 + hdr_off)
|
||||
if page2.magic == SLAB_MAGIC:
|
||||
if _slab_page_owns(page2, ptr, hdr_off) != 0:
|
||||
os2: uint16 = page2.obj_size
|
||||
if os2 == 32 or os2 == 64 or os2 == 128 or os2 == 256 or os2 == 512 or os2 == 1024:
|
||||
return page2
|
||||
@@ -373,15 +422,19 @@ class _SlabAllocator:
|
||||
page.block_ptr = block
|
||||
page.next = None
|
||||
page.prev = None
|
||||
for bi in range(SLAB_BITMAP_BYTES):
|
||||
page.alloc_bits[bi] = 0
|
||||
data_start: uint64 = uint64(block) + d_off
|
||||
# 加固:下面那个建链循环是分配器里唯一"成片写指针"的地方,一旦 total 因下溢/截断而
|
||||
# 离谱,就会把 nxt 指针写到自己页外、覆盖相邻块头。先把 total 与地址范围校验成自洽。
|
||||
if block.size < uint64(d_off) + uint64(aligned_size):
|
||||
serial.puts("[mm] slab _create_page: block too small for page header - refused\n")
|
||||
page.magic = 0 # 归还前抹掉页头,避免后续把它误认成有效 slab 页
|
||||
BuddySystemObject.free_block(block)
|
||||
return None
|
||||
if uint64(total) * uint64(aligned_size) + uint64(d_off) > block.size:
|
||||
serial.puts("[mm] slab _create_page: total*obj_size+d_off > block.size (would write outside page) - refused\n")
|
||||
page.magic = 0 # 同上
|
||||
BuddySystemObject.free_block(block)
|
||||
return None
|
||||
page.free_head = rawptr(data_start)
|
||||
@@ -406,11 +459,29 @@ class _SlabAllocator:
|
||||
self.caches[size_class].prev = page
|
||||
self.caches[size_class] = page
|
||||
obj: rawptr = page.free_head
|
||||
_slab_bit_set(page, _slab_bit_index(page, obj))
|
||||
page.free_head = _slab_read_ptr(obj)
|
||||
page.free_count -= 1
|
||||
return obj
|
||||
|
||||
def free_obj(self, ptr: rawptr, page: slab_page*):
|
||||
# 只有“已分配”的对象能被 free:否则 free_count 会提前到 total_objs,页被拆回
|
||||
# buddy 而页内仍有活对象(后续 free 只看到 magic=0 的旧页头)。
|
||||
bi: int = _slab_bit_index(page, ptr)
|
||||
if _slab_bit_test(page, bi) == 0:
|
||||
_fd: [112]char
|
||||
viperlib.snprintf(&_fd, 112, "[mm] slab free_obj: page=0x%lx ptr=0x%lx idx=%d not allocated - refused\n",
|
||||
uint64(page), uint64(ptr), bi)
|
||||
serial.puts(&_fd)
|
||||
return
|
||||
# 已全空的页也不能再收对象(计数/指针已不可信)
|
||||
if page.free_count >= page.total_objs:
|
||||
_fo: [96]char
|
||||
viperlib.snprintf(&_fo, 96, "[mm] slab free_obj: page=0x%lx free=%d total=%d - refused\n",
|
||||
uint64(page), int(page.free_count), int(page.total_objs))
|
||||
serial.puts(&_fo)
|
||||
return
|
||||
_slab_bit_clear(page, bi)
|
||||
_slab_write_ptr(ptr, page.free_head)
|
||||
page.free_head = ptr
|
||||
page.free_count += 1
|
||||
|
||||
Reference in new issue
Block a user