[db:标题]
摘要:Lab: system calls 	在这个lab当中6.1810Fall 2025 它要求你在xv6当中添加一个新的系统调用,以此来帮助你理解在操作系统当中,系统调用的底层实现逻辑和调用链条; 	
Lab: system calls
在这个lab当中6.1810 / Fall 2025 它要求你在xv6当中添加一个新的系统调用,以此来帮助你理解在操作系统当中,系统调用的底层实现逻辑和调用链条;
之后该lab当中会告诉你一个故意留下来的系统漏洞,要求你利用该漏洞获取之前的进程(已经被清理的进程)的私有数据,通过此lab你可以学到操作系统是如何隔离每个进程的,同时也会告诉你在回收进程的资源时如果处理不当会导致原本应该被清理的进程,它的私有数据可能会被其他进程窃取,从而打破了操作系统的进程隔离机制。
1.Using gdb
这一部分涉及gdb的调试,所以我们暂时跳过,更多GDB的调试技巧可以去网上搜索一下,这里就不再阐述了。
2.Sandbox a command(中等难度)
在这一小节当中,我们需要给xv6操作系统引入一种进程级系统调用限制机制(sandbox)。具体而言,允许用户进程通过一个新的系统调用 interpose(mask, path),为当前进程及其子进程设置一组“被禁止的系统调用”,使得后续执行中一旦触发这些系统调用,就会被内核拒绝。官网当中告诉我们interpose接收两个参数,一个是是屏蔽掩码mask,另一个是路径path (当前用不到)。
来自官网的提示(个人解析版):
在 Makefile 中向 UPROGS 添加 $U/_sandbox,以保证编译器会编译该源文件。
由于interpose没有任何声明和实现,所以要在user/user.h 中添加一个 interpose 原型(不要遗漏参数) 。
在user/usys.pl当中增加一个新的项,该文件是用户态系统调用接口的生成脚本,它会帮助生成一个汇编文件user/usys.S,该文件中指定了每一个系统调用的参数,陷入指令和返回指令。
因为interpose是一个新的系统调用,所以我们要在kernel/syscall.h当中添加一个新的系统调用码,用于之后syscall函数的使用。
因为要添加一个新的系统调用,所以我们要严格按照xv6关于系统调用函数声明的规范进行命名,我们可以参考xv6当中已有的函数声明,所以我们在kernel/sysproc.c 当中实现一个名为:sys_interpose(void)的函数,它就是最终的调用实现。
按照官网的要求,我们的屏蔽掩码需要父进程传递给子进程(或者说是子进程继承了父进程的屏蔽掩码),所以这就代表了这个屏蔽掩码需要被持久存储于进程中,于是我们需要在进程的结构体当中添加一个字段,用于记录屏蔽掩码,同时因为子进程是父进程通过调用fork创造出来的,所以一定存在一个函数用于将父进程当中某些状态/属性纹丝不动地赋值给子进程当中对应的字段,因此根据官网的提示,我们可以在kernel/proc.c当中找到一个名为:kfork的函数,这里就是父子进行状态/属性继承的地方,我们需要在这里修改一下,使得其可以将父进程新添加的“屏蔽掩码”字段同样赋值给子进程。
因为每个系统调用都是一个函数指针,所以在kernel/syscall.c当中,有一个数组: syscalls,里面存放的是每一个系统调用的入口地址,我们需要在该数组当中添加一项新的数据,同时需要在此文件中添加sys_interpose的声明(可以参考已有的xv6代码,照葫芦画瓢)。
因为我们要实现的是系统调用的屏蔽机制,所以在xv6当中,任何系统调用最终都会通过内核态函数syscall进行调用号的识别和分发调用,所以我们可以在此函数当中添加某些判断逻辑,通过将当前请求系统调用的进程当中的屏蔽掩码与当前进程请求的系统调用的调用码向比对来得到是否要屏蔽该系统调用。
以下是代码相关内容:
##user/user.h中新增的内容(用户态函数声明):
int interpose(int,char *path);
##user/usys.pl中新增的内容:
entry("interpose");
##kernel/syscall.h中新增的内容(系统调用号):
#define SYS_interpose 22 //interpose的系统调用码
##kernel/proc.c/kfork函数体内,中新增的内容(子进程继承父进程的mask):
... ...
/*修改点,父进程的状态mask传递给子进程
* 父进程的mask已经被修改,此时若创建新
* 的子进程则mask也要一并传递。
