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

fix(video): 增加了对frame buffer操作的安全检查
This commit is contained in:
曾俊 2024-11-10 23:46:56 +08:00 committed by GitHub
parent 5d54e74768
commit f5c732d8dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 171 additions and 74 deletions

View File

@ -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;
}
}
}

View File

@ -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=600current_xpos=200*3+当前行已经渲染像素数*3limit_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;
}