在lab 4的分支里面,会有各种好玩的用户程序.如下:
觉得还是有必要一一对其进行简要的分析.自顶向下的了解OS的机制
分析的用户程序顺序随意,不按照难度排序
badsegment.c:
这里的唯一一行嵌入式汇编尝试把 0x28这个数赋值给数据段寄存器 DS
看这里Global describe table -- gdt,
0x28就是对于CPU0来说的TSS0段的选择子.
把TSS0选择子移入ds数据寄存器是违法的,会触发异常保护T_GPFLT
修改各种段寄存器都要满足CPL和DPL关系条件的.必须触发系统调用,进入内核态,才能修改.
当前用户态的段CPL是3,那么就只能修改DPL是3的权限段,不能比这个权限高的,我们这里尝试修改原来的测试badsegment.c
如下:我该成了0x20,用户的数据段描述符.
运行情况,如下:
没有panic了....yes. 继续
breakpoint.c:
int $3尝试调用3号中断.
下面是 运行后挂掉的 trap frame
buggyhello.c:
这里尝试通过系统调用打印地址 0x1出的一个字符.系统完成启动之后,0x200000以下的内容都是empty memory.
这里太明显了,去访问没有映射的指针,会被我们之前lab4写好的 mem_check里的断言挂掉
buggyhello2.c:
这里尝试输出hello标记地址处的字符串,从注释里面会发现,作者的意图是直接销毁这个进程,但是,这里由于我们之前又写了断言在sys_cputs里面,于是这里要注释掉这行断言你才能看到预期的输出
输出如下:
divezero.c:
单步调试跟进去看:
是在指令ldiv的时候触发divide_error的
evilhello.c:
邪恶的hello,究竟怎么个邪恶法.
这里尝试用系统调用输出 0xf010000c地址出的100个字符.
我们知道虚拟地址0xf010000c对应的物理地址是0x10000c,这是刚开始的时候内核的入口地址
由于之前又做好了错误检查的部分.这里需要注释掉下图中的注释部分,不然访问内核高地址的尝试都没门..
这里需要把两个检测项都注释掉,因为,对于用户来说不仅仅内核高地址没有 PTE_U而且没有PTE_P,
输出会发现一开始还是尝试去解释这块地址出的一些数据,解释成字符,不过都是乱码.尔后进程被强制释放.
faultread.c:
这里我们初学C语言的时候,指针没学好,或警惕性不高的时候,没进行非空检查的话,就可能对NULL指针dereference.这是非法的.
上面这个测试程序会直接触发异常,然后挂掉系统.这里我们还没进行到lab 4,没有构建起用户空间触发的page fault的异常处理机制.后续到lab 4我会特别把这里例子再单独开一贴分析
faultreadkernel.c:
下面是尝试读取内核区数据的例子,用户程序是不允许访问这里的数据的
可以和上面那个 faultread.c做比较,上面的是访问0x00这个地址没有映射,于是出现的问题是确实PTE_P
这里的内核地址是有PTE_P,但是这里是不允许用户访问的.protection. 同是page fault,他们之间还是有稍许区别的.
关于 faultwrite.c faultwritekernel.c的分析和上述 faultread.c faultreadkernel.c同理.不再赘述.
hello.c:
深深的无力,感觉没啥好说的,这里就是访问thisenv这个全局变量,指向当前进程的struct env
打印thisenv->env_id.相当于Linux里面的输出进程pid : )
值得说一下的是这里的输出信息,先从内核态通过trap进入了用户态打印输出了hello, world
而后由于thisenv指向的结构体都是由刚开始分配内存的时候envs指向的一连串结构体储存在内核地址内,于是这里需要访问内核,势必触发trap,进入内核态,访问并打印thisenv指针指向的结构体内的成员 env_id
testbss.c:
我们知道,未初始化的的全局变量会放入到可执行程序的bss段.
这里的bigarray就是个例子.分配了很大的空间这个为初始化的全局变量
程序先测试验证,全局未初始化的变量都默认初始值为0.
然后一个个赋值.检测也没问题.前面三个for循环都正常
尝试在申请的数组范围之外写入数据就会触发page fault.因此我们看不到后面的panic输出信息.(这还是在lab3的基础上的分析,因为这里没有用户态的page fault handler,如果是lab 4的话,系统不会这样挂掉,但是如果正常的处理机制还是不应该让程序继续运行了 : )
lab 3的分析告一段落~