Browse Source

fix(video): 增加了对frame buffer操作的安全检查 (#1034)

fix(video): 增加了对frame buffer操作的安全检查
曾俊 5 months ago
parent
commit
f5c732d8dc

+ 44 - 73
kernel/src/driver/video/fbdev/base/mod.rs

@@ -1,4 +1,5 @@
 use alloc::{string::String, sync::Arc, vec::Vec};
+use render_helper::{FrameP, FramePointerStatus};
 use system_error::SystemError;
 
 use crate::{
@@ -69,22 +70,20 @@ pub trait FrameBuffer: FrameBufferInfo + FrameBufferOps + Device {
         let bit_per_pixel = self.current_fb_var().bits_per_pixel;
 
         // 计算图像在帧缓冲中的起始位
-        let mut bitstart = (y * self.current_fb_fix().line_length * 8) + (x * bit_per_pixel);
+        let bitstart = (y * self.current_fb_fix().line_length * 8) + (x * bit_per_pixel);
         let start_index = bitstart & (32 - 1);
         let pitch_index = (self.current_fb_fix().line_length & (byte_per_pixel - 1)) * 8;
-        // 位转字节
-        bitstart /= 8;
-
-        // 对齐到像素字节大小
-        bitstart &= !(byte_per_pixel - 1);
-
-        let dst1 = boot_param.screen_info.lfb_virt_base;
-        if dst1.is_none() {
+        let dst2 = boot_param.screen_info.lfb_virt_base;
+        if dst2.is_none() {
             return;
         }
-        let mut dst1 = dst1.unwrap();
-        dst1 += VirtAddr::new(bitstart as usize);
-
+        let mut safe_pointer = FrameP::new(
+            self.current_fb_var().yres as usize,
+            self.current_fb_var().xres as usize,
+            self.current_fb_var().bits_per_pixel as usize,
+            dst2.unwrap(),
+            image,
+        );
         let _ = self.fb_sync();
 
         if image.depth == 1 {
@@ -107,16 +106,9 @@ pub trait FrameBuffer: FrameBufferInfo + FrameBufferOps + Device {
                 && image.width & (32 / bit_per_pixel - 1) == 0
                 && (8..=32).contains(&bit_per_pixel)
             {
-                unsafe { self.fast_imageblit(image, dst1, fg, bg) }
+                unsafe { self.fast_imageblit(image, &mut safe_pointer, fg, bg) }
             } else {
-                self.slow_imageblit(
-                    image,
-                    dst1,
-                    fg,
-                    bg,
-                    bitstart / 4,
-                    self.current_fb_fix().line_length,
-                )
+                self.slow_imageblit(image, &mut safe_pointer, fg, bg)
             }
         } else {
             todo!("color image blit todo");
@@ -129,7 +121,7 @@ pub trait FrameBuffer: FrameBufferInfo + FrameBufferOps + Device {
     /// 要求 image->width 可以被像素或 dword (ppw) 整除。
     /// 要求 fix->line_length 可以被 4 整除。
     /// 扫描线的开始和结束都是 dword 对齐的。
-    unsafe fn fast_imageblit(&self, image: &FbImage, mut dst1: VirtAddr, fg: u32, bg: u32) {
+    unsafe fn fast_imageblit(&self, image: &FbImage, dst1: &mut FrameP, fg: u32, bg: u32) {
         let bpp = self.current_fb_var().bits_per_pixel;
         let mut fgx = fg;
         let mut bgx = bg;
@@ -161,13 +153,12 @@ pub trait FrameBuffer: FrameBufferInfo + FrameBufferOps + Device {
             color_tab[idx] = (*val & eorx) ^ bgx;
         }
 
-        let mut dst;
         let mut shift;
         let mut src;
         let mut offset = 0;
         let mut j = 0;
+        let mut count = 0;
         for _ in (0..image.height).rev() {
-            dst = dst1.as_ptr::<u32>();
             shift = 8;
             src = offset;
             match ppw {
@@ -175,10 +166,8 @@ pub trait FrameBuffer: FrameBufferInfo + FrameBufferOps + Device {
                     // 8bpp
                     j = k;
                     while j >= 2 {
-                        *dst = color_tab[(image.data[src] as usize >> 4) & bitmask];
-                        dst = dst.add(1);
-                        *dst = color_tab[(image.data[src] as usize) & bitmask];
-                        dst = dst.add(1);
+                        dst1.write(color_tab[(image.data[src] as usize >> 4) & bitmask]);
+                        dst1.write(color_tab[(image.data[src] as usize) & bitmask]);
                         j -= 2;
                         src += 1;
                     }
@@ -187,14 +176,10 @@ pub trait FrameBuffer: FrameBufferInfo + FrameBufferOps + Device {
                     // 16bpp
                     j = k;
                     while j >= 4 {
-                        *dst = color_tab[(image.data[src] as usize >> 6) & bitmask];
-                        dst = dst.add(1);
-                        *dst = color_tab[(image.data[src] as usize >> 4) & bitmask];
-                        dst = dst.add(1);
-                        *dst = color_tab[(image.data[src] as usize >> 2) & bitmask];
-                        dst = dst.add(1);
-                        *dst = color_tab[(image.data[src] as usize) & bitmask];
-                        dst = dst.add(1);
+                        dst1.write(color_tab[(image.data[src] as usize >> 6) & bitmask]);
+                        dst1.write(color_tab[(image.data[src] as usize >> 4) & bitmask]);
+                        dst1.write(color_tab[(image.data[src] as usize >> 2) & bitmask]);
+                        dst1.write(color_tab[(image.data[src] as usize) & bitmask]);
                         src += 1;
                         j -= 4;
                     }
@@ -203,22 +188,14 @@ pub trait FrameBuffer: FrameBufferInfo + FrameBufferOps + Device {
                     // 32 bpp
                     j = k;
                     while j >= 8 {
-                        *dst = color_tab[(image.data[src] as usize >> 7) & bitmask];
-                        dst = dst.add(1);
-                        *dst = color_tab[(image.data[src] as usize >> 6) & bitmask];
-                        dst = dst.add(1);
-                        *dst = color_tab[(image.data[src] as usize >> 5) & bitmask];
-                        dst = dst.add(1);
-                        *dst = color_tab[(image.data[src] as usize >> 4) & bitmask];
-                        dst = dst.add(1);
-                        *dst = color_tab[(image.data[src] as usize >> 3) & bitmask];
-                        dst = dst.add(1);
-                        *dst = color_tab[(image.data[src] as usize >> 2) & bitmask];
-                        dst = dst.add(1);
-                        *dst = color_tab[(image.data[src] as usize >> 1) & bitmask];
-                        dst = dst.add(1);
-                        *dst = color_tab[(image.data[src] as usize) & bitmask];
-                        dst = dst.add(1);
+                        dst1.write(color_tab[(image.data[src] as usize >> 7) & bitmask]);
+                        dst1.write(color_tab[(image.data[src] as usize >> 6) & bitmask]);
+                        dst1.write(color_tab[(image.data[src] as usize >> 5) & bitmask]);
+                        dst1.write(color_tab[(image.data[src] as usize >> 4) & bitmask]);
+                        dst1.write(color_tab[(image.data[src] as usize >> 3) & bitmask]);
+                        dst1.write(color_tab[(image.data[src] as usize >> 2) & bitmask]);
+                        dst1.write(color_tab[(image.data[src] as usize >> 1) & bitmask]);
+                        dst1.write(color_tab[(image.data[src] as usize) & bitmask]);
                         src += 1;
                         j -= 8;
                     }
@@ -233,8 +210,7 @@ pub trait FrameBuffer: FrameBufferInfo + FrameBufferOps + Device {
              */
             while j != 0 {
                 shift -= ppw;
-                *dst = color_tab[(image.data[src] as usize >> shift) & bitmask];
-                dst = dst.add(1);
+                dst1.write(color_tab[(image.data[src] as usize >> shift) & bitmask]);
                 if shift == 0 {
                     shift = 8;
                     src += 1;
@@ -242,22 +218,15 @@ pub trait FrameBuffer: FrameBufferInfo + FrameBufferOps + Device {
                 j -= 1;
             }
 
-            dst1 += VirtAddr::new(self.current_fb_fix().line_length as usize);
+            count += 1;
+            dst1.move_with_offset(self.current_fb_fix().line_length * count);
             offset += spitch as usize;
         }
     }
 
-    fn slow_imageblit(
-        &self,
-        _image: &FbImage,
-        _dst1: VirtAddr,
-        _fg: u32,
-        _bg: u32,
-        _start_index: u32,
-        _pitch_index: u32,
-    ) {
-        let mut dst = _dst1.as_ptr::<u32>();
+    fn slow_imageblit(&self, _image: &FbImage, safe_dst: &mut FrameP, _fg: u32, _bg: u32) {
         let mut count = 0;
+        let mut pt_status = FramePointerStatus::Normal;
         let iter = BitIter::new(
             _fg,
             _bg,
@@ -268,17 +237,19 @@ pub trait FrameBuffer: FrameBufferInfo + FrameBufferOps + Device {
             _image.width,
         );
         for (content, full) in iter {
-            unsafe {
-                *dst = content;
-
-                dst = dst.add(1);
+            match pt_status {
+                FramePointerStatus::OutOfBuffer => {
+                    return;
+                }
+                FramePointerStatus::OutOfScreen => {}
+                FramePointerStatus::Normal => {
+                    pt_status = safe_dst.write(content);
+                }
             }
-
             if full {
                 count += 1;
-                dst = unsafe {
-                    _dst1.as_ptr::<u8>().add((_pitch_index * count) as usize) as *mut u32
-                };
+                safe_dst.move_with_offset(self.current_fb_fix().line_length * count);
+                pt_status = FramePointerStatus::Normal;
             }
         }
     }

+ 127 - 1
kernel/src/driver/video/fbdev/base/render_helper.rs

@@ -1,4 +1,8 @@
-use core::slice::Iter;
+use core::{ops::Add, slice::Iter};
+
+use crate::mm::VirtAddr;
+
+use super::FbImage;
 
 pub struct BitIter<'a> {
     fgcolor: u32,
@@ -170,3 +174,125 @@ impl PixelLineStatus {
         }
     }
 }
+
+/// # 结构功能
+/// 安全的FrameBufferPointer
+/// 使用该结构体访问FrameBuffer可以防止超出FrameBuffer区域的访问
+/// 需要注意,使用该指针写入时,任何超出屏幕的写入都是无效的!即使仍然可以写入显存。
+/// 此外由于FbImage中的x和y变量采用u32类型,所以并未考虑左越界和上越界的安全性(即Image.x<0或Image.y<0的情况)
+/// ## 成员
+///
+///  - "dst" : 显存base address,通常是0xffffa1003ff00000
+///  - "limit" : 显存区域上界,可以通过公式计算:limit = dst + 分辨率高*分辨率宽*每个像素的**字节**数。也就是说任何对于显存的访问应该限制在[dst,limit)中
+///  - "current" : 当前相对于start_offset的位移
+///  - "start_offset" : 如果你要渲染某个Image,你可能不是总是从屏幕左上角(0,0)开始渲染,你可能从某个offset开始
+///  - "start_xpos" : 表示你要渲染的Image的x位置的字节位置
+///  - "current_xpos" : 当前渲染的x位置的字节位置
+///  - "limit_xpos" : 最大的渲染x位置的字节位置。 例:假设系统的分辨率为640,位深为24,你需要渲染的Image的x坐标为200,那么start_xpos=200*3=600,current_xpos=200*3+当前行已经渲染像素数*3,limit_xpos=640*3
+#[derive(Debug)]
+pub struct FrameP {
+    dst: VirtAddr,
+    limit: VirtAddr,
+    current: usize,
+    start_offset: usize,
+    start_xpos: usize,
+    current_xpos: usize,
+    limit_xpos: usize,
+}
+
+impl FrameP {
+    pub fn new(
+        frame_height: usize,
+        frame_width: usize,
+        bitdepth: usize,
+        dst: VirtAddr,
+        image: &FbImage,
+    ) -> Self {
+        let byte_per_pixel = bitdepth / 8;
+        let limit = VirtAddr::new(frame_height * frame_width * byte_per_pixel) + dst;
+        Self {
+            dst,
+            limit,
+            current: 0,
+            start_offset: start_offset(image, bitdepth as u32, (frame_width * bitdepth / 8) as u32)
+                as usize,
+            start_xpos: image.x as usize * byte_per_pixel,
+            current_xpos: image.x as usize * byte_per_pixel,
+            limit_xpos: frame_width * byte_per_pixel,
+        }
+    }
+
+    /// # 函数功能
+    /// 写入某个数据并将指针增大
+    pub fn write<T>(&mut self, data: T) -> FramePointerStatus {
+        // 首先获取数据大小
+        let size = size_of::<T>();
+        // 复制显存指针防止改变self的dst
+        let mut dst = self.dst;
+
+        // 你最终要写入的位置实际上是[dst+start_offset+current,dst+start_offset+current+size),所以我们要确定你写入的位置是否超过limit
+        if self.dst.data() + self.current + self.start_offset + size > self.limit.data() {
+            return FramePointerStatus::OutOfBuffer;
+        }
+        // 我们也不希望你的x超出屏幕右边,超出屏幕右边的部分会被忽略掉,因为如果写入显存会导致内存风险
+        else if self.current_xpos + size > self.limit_xpos {
+            return FramePointerStatus::OutOfScreen;
+        }
+        // 如果上面两个检查都通过了,我们就可以写入显存了
+        else {
+            // 这里是写入位置的实际虚拟地址
+            dst = dst.add(self.current + self.start_offset);
+        }
+        // 写入操作
+        unsafe {
+            *dst.as_ptr::<T>() = data;
+        }
+        // 写入后更新current和xpos
+        self.current += size;
+        self.current_xpos += size;
+        // 由于写入正常,我们返回正常的状态
+        return FramePointerStatus::Normal;
+    }
+
+    /// # 函数功能
+    /// 移动指针**至**某个offset
+    /// todo: 当前函数应当只用于换行,否则可能会导致安全性问题,即offset应该是每行像素的开头
+    pub fn move_with_offset(&mut self, offset: u32) {
+        self.current = offset as usize;
+        // let x_pos=self.current%self.limit_xpos;
+        // match x_pos{
+        //     n if n<self.start_xpos=>{
+        //         // send_to_default_serial8250_port(format!("Sended by function move_with_offset: Check if there is misusage of offset,the image.x is:{:?} while the xpos indicated by the offset is:{:?},current FP:{:?}\n",self.start_offset,x_pos,self).as_bytes());
+        //     }
+        //     n if n>=self.limit_xpos=>{
+        //         // send_to_default_serial8250_port(format!("Sended by function move_with_offset: Check if there is misusage of offset,The offset:{:?} is so large that it would exceed the limit of frame buffer\n",offset).as_bytes());
+        //     }
+        //     _=>{
+
+        //     }
+        // }
+        self.current_xpos = self.start_xpos;
+    }
+}
+
+pub enum FramePointerStatus {
+    /// 表示状态正常
+    Normal,
+    /// 超出屏幕,一直到换行时才应该恢复到正常状态
+    OutOfScreen,
+    /// 超出缓存,此时应当立即停止渲染
+    OutOfBuffer,
+}
+
+pub fn start_offset(image: &FbImage, bitdepth: u32, line_length: u32) -> u32 {
+    let x = image.x;
+    let y = image.y;
+    let mut bitstart = (y * line_length * 8) + (x * bitdepth);
+    let byte_per_pixel = core::mem::size_of::<u32>() as u32;
+    // 位转字节
+    bitstart /= 8;
+
+    // 对齐到像素字节大小
+    bitstart &= !(byte_per_pixel - 1);
+    return bitstart;
+}