环境

还是xv6-public

必做+选做1&2

  1. 完成系统调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// syscall.h 添加
#define SYS_alarm 24

// user.h 添加
int alarm(int ticks, void (*handler)());

// usys.S 添加
SYSCALL(alarm)

// syscall.c 添加
extern int sys_alarm(void);
[SYS_alarm] sys_alarm, // syscalls[] 数组
[SYS_alarm] "alarm", // syscall_names[] 数组

// proc.h中添加进程字段(struct proc)
int alarmticks; // 报警间隔(多少个 tick 触发一次)
int alarmticksleft; // 距离下一次触发还剩多少 tick
void (*alarmhandler)(); // 报警处理函数指针
int alarm_in_handler; // 是否正在执行 handler(防止重入)
uint alarm_eip; // 保存的返回地址
struct trapframe *alarm_tf;

// proc.c 添加初始化进程字段
// allocproc
p->alarmticks = 0;
p->alarmticksleft = 0;
p->alarmhandler = 0;
p->alarm_in_handler = 0;
p->alarm_eip = 0;
p->alarm_tf = 0;
// fork
np->alarmticks = curproc->alarmticks;
np->alarmticksleft = curproc->alarmticksleft;
np->alarmhandler = curproc->alarmhandler;
np->alarm_in_handler = 0; // 子进程不在 handler 中
np->alarm_eip = 0; // 子进程无保存的 eip
np->alarm_tf = 0;
// sysproc.c 中添加实现 sys_alarm
int
sys_alarm(void)
{
int ticks;
void (*handler)();

if(argint(0, &ticks) < 0)
return -1;
if(argptr(1, (char**)&handler, 1) < 0)
return -1;

cprintf("sys_alarm: ticks=%d, handler=%x\n", ticks, (uint)handler);

curproc->alarmticks = ticks;
curproc->alarmticksleft = ticks;
curproc->alarmhandler = handler;
curproc->alarm_in_handler = 0;
curproc->alarm_eip = 0;

return 0;
}
// 修改 trap.c 中
// trap()
/*前面的部分原代码
case T_IRQ0 + IRQ_TIMER:
if(cpuid() == 0){
acquire(&tickslock);
ticks++;
wakeup(&ticks);
release(&tickslock);
}
*/
//alarm
if(myproc() != 0 && (tf->cs & 3) == 3){
struct proc *p = myproc();
if(p->alarmticks > 0 && p->alarm_in_handler == 0){
p->alarmticksleft--;
// cprintf("DEBUG: ticksleft=%d\n", p->alarmticksleft);
if(p->alarmticksleft == 0){
// cprintf("DEBUG: TRIGGERING ALARM!\n");
p->alarmticksleft = p->alarmticks;
p->alarm_in_handler = 1;

if(p->alarm_tf == 0){
p->alarm_tf = (struct trapframe*)kalloc();
if(p->alarm_tf == 0){
panic("alarm: kalloc failed");
}
}
memmove(p->alarm_tf, tf, sizeof(struct trapframe));

p->alarm_eip = tf->eip;
tf->eip = (uint)p->alarmhandler;
}
}
}
//从 alarm handler 返回后恢复 eip
struct proc *p = myproc();
if(p != 0 && p->alarm_in_handler == 1 && p->alarm_eip != 0){
if(tf->eip != (uint)p->alarmhandler){
if(p->alarm_tf != 0){
memmove(tf, p->alarm_tf, sizeof(struct trapframe));
tf->eip = p->alarm_eip;
kfree((char*)p->alarm_tf);
p->alarm_tf = 0;
} else {
tf->eip = p->alarm_eip;
}
p->alarm_eip = 0;
p->alarm_in_handler = 0;
}
}
  1. 创建 alarmtest.c 测试程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "types.h"
#include "stat.h"
#include "user.h"

void periodic()
{
write(2, "alarm!\n", 7);
}

int
main(int argc, char *argv[])
{
int i;
printf(1, "alarmtest starting\n");
alarm(10, periodic);
for(i = 0; i < 50*500000; i++){
if((i % 1000000) == 0)
write(2, ".", 1);
}
write(2, "\nDone\n", 6);
exit();
}
  1. 修改 Makefile,在 UPROGS 中添加
1
_alarmtest\
  1. 编译运行(Ctrl+a,再x退出qemu)
1
2
3
4
5
make clean
make
make CPUS=1 qemu-nox
# 在xv6中测试:
alarmtest

选做3

  1. 创建攻击程序attack.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include "types.h"
#include "stat.h"
#include "user.h"

void (*evil_handler)() = (void(*)())0xffffffff;

int
main(int argc, char *argv[])
{
printf(1, "=== Security Attack Test ===\n");
printf(1, "Attempting to set alarm with invalid handler: 0x%x\n",
(unsigned int)evil_handler);

int ret = alarm(1, evil_handler);

if(ret < 0){
printf(1, "alarm rejected! Security check works!\n");
} else {
printf(1, "alarm accepted (VULNERABLE), waiting...\n");
// 等待 alarm 触发(如果被接受)
int i;
for(i = 0; i < 5000000; i++);
}

printf(1, "Attack test completed\n");
exit();
}
  1. 更改Makefile
1
_attack\
  1. 编译并测试,漏洞存在

  2. 修改代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 改sysproc.c,在 sys_alarm 中添加检查,同时注释之前的cprintf
// cprintf("sys_alarm: ticks=%d, handler=%x\n", ticks, (uint)handler);

struct proc *curproc = myproc();

if((uint)handler >= curproc->sz || (uint)handler < 4096){
cprintf("alarm: SECURITY: invalid handler address 0x%x (sz=0x%x), rejecting\n",
(uint)handler, curproc->sz);
return -1;
}

// 修改改trap.c,在触发时添加运行时检查 (alarm中)
//alarm(之前添加的)
if(myproc() != 0 && (tf->cs & 3) == 3){
struct proc *p = myproc();
if(p->alarmticks > 0 && p->alarm_in_handler == 0){
p->alarmticksleft--;
// cprintf("DEBUG: ticksleft=%d\n", p->alarmticksleft);
if(p->alarmticksleft == 0){
// cprintf("DEBUG: TRIGGERING ALARM!\n");
p->alarmticksleft = p->alarmticks;
p->alarm_in_handler = 1;

// 安全检查(新添加)
int safe = 1;
if((uint)p->alarmhandler >= p->sz || (uint)p->alarmhandler < 4096){
cprintf("alarm: SECURITY: invalid handler 0x%x (sz=0x%x), killing\n",
(uint)p->alarmhandler, p->sz);
safe = 0;
}

if(tf->esp >= p->sz || tf->esp < 4096){
cprintf("alarm: SECURITY: invalid stack pointer 0x%x (sz=0x%x), killing\n",
tf->esp, p->sz);
safe = 0;
}

if(!safe){
p->killed = 1;
p->alarm_in_handler = 0;
p->alarmticksleft = 0;
lapiceoi();
break;
}
  1. 编译测试