第28讲 · 操作系统的安全

2026 南京大学《操作系统原理》第二十八讲 · 课堂实录重构

主讲 蒋炎岩(Yanyan Jiang)
视频 https://www.bilibili.com/video/BV1qkEX6FEb5/
课程主页 https://jyywiki.cn/OS/2026/
本讲讲义 https://jyywiki.cn/OS/2026/lect28.md
讲义 / 幻灯片作者 蒋炎岩,https://jyywiki.cn/
讲义 / 幻灯片许可 CC BY-NC 4.0
图片来源 书中插图为课堂演示与讲义渲染图;演示类截图为视频画面

对应讲次:第28讲(南京大学 操作系统原理)

制作模型:deepseek-flash(coding-agent / pi;据 ~/.pi/agent/settings.json)

编者说明:本书由课堂录像的语音字幕重构而成,术语、章节骨架与参考链接已按课程官方讲义(lect28.md)校准。讲师的口语风格、类比与案例被尽量保留;编辑性的补充一律以「编者说明」或「编者拓展」标出。本讲为补充内容,不在考试范围。


引子:早期计算机系统「完全没有任何安全可言」

(参考时间: 00:00)

这是补充内容(不考试):计算机系统安全、容器虚拟化、以及 Agent 时代的沙箱。

计算机系统早期完全没有安全:

早期 BBS(小百合/水木):用户名密码明文传输,几乎没有安全意识

📷 早期 BBS(小百合/水木):用户名密码明文传输,几乎没有安全意识
回到原视频 (00:03:00)

编者拓展|讲师的小故事:「当时我们每天最大的乐趣就是看全站十大热门话题……那时候小百合是没有任何防护的,我可以写脚本疯狂尝试密码登录。」——当互联网开始涉及个人隐私、支付(支付宝),安全性就变得非常重要。


一、安全的三个方面

(参考时间: 00:05)

维度 含义
Confidentiality(机密性/保密性) 你不想给别人看的,别人就看不到
Integrity(完整性) 你不想让别人改的,别人就改不了
Availability(可用性) 你自己能用的服务,不会被别人弄得用不了
安全三要素:Confidentiality / Integrity / Availability

📷 安全三要素:Confidentiality / Integrity / Availability
回到原视频 (00:06:00)

例子:

DoS(Denial of Service):如果你通过入侵拿到了 1 万台机器的权限,就可以同时向 DeepSeek 发起请求。讲师现场演示:让模型从 1 数到一个 100K 的随机数字——「模型平时会拒绝你说『数字太大了』,但开启思考时它会把问题重复一遍、把那个数字抄一遍」——于是 1 万台肉鸡就能瞬间瘫痪服务。

DoS:利用「思维链会重复问题」的特性,用海量请求瞬间瘫痪服务

📷 DoS:利用「思维链会重复问题」的特性,用海量请求瞬间瘫痪服务
回到原视频 (00:10:30)

编者拓展|算法复杂性攻击(algorithmic complexity attack):精心构造很多字符串,让它们都 hash 到同一个地址——无论开放寻址还是拉链表,hash table 都会退化成线性时间。防御:当链表大到一定程度换成树(Java 8 的 HashMap 就这么做了)。另一种是给服务发超长请求,「在云上租几个主机就能瘫痪一个网络服务」。

算法复杂性攻击:精心构造的字符串全部 hash 到同一地址,让 hash table 退化成线性

📷 算法复杂性攻击:精心构造的字符串全部 hash 到同一地址,让 hash table 退化成线性
回到原视频 (00:12:00)


二、密码学极简介绍

(参考时间: 00:13)

One-way function(单向函数):正向好算、逆向极难:

用途:验证文件完整性(网站公布 sha256sum,下载后自己校验)。

One-way function:正向好算、逆向极难、难碰撞(`sha256`/`MD5`)

📷 One-way function:正向好算、逆向极难、难碰撞(`sha256`/`MD5`)
回到原视频 (00:15:00)

Trapdoor function(陷门函数):正向容易、反向困难,但如果有一个「后门」 $T$ 就变容易——这正是公钥/私钥(.ssh/id_rsa 是私钥/陷门,.pub 是公开的函数):

编者拓展|区块链:一个「不可摧毁」的 append-only 数据结构:如果有一个全世界共同维护、只有一份的 append-only 数据结构,就可以用来记账——每次 append 都算一个(包含所有前缀的)hash 并签名。(实际实现用树状结构快速维护前缀哈希。)共识靠 proof of work:「谁花的时间多谁就能捡到骨头」——靠单向函数确保 hash value 足够小;分叉时谁更长听谁的。甚至可以用一个图灵完备的虚拟机运行代码(写着「从哪个钱包给哪个钱包转多少钱」),所有人 replay 就能知道谁有多少钱。「它带来绝对的 availability——全世界都是 replica,只要都信任这个协议——但它没有任何 confidentiality。」

Blockchain:一个全球共同维护、不可摧毁的 append-only 数据结构(proof of work)

📷 Blockchain:一个全球共同维护、不可摧毁的 append-only 数据结构(proof of work)
回到原视频 (00:19:30)

编者拓展|「24 个英文单词」:讲师提到——「有些人进去的时候,在住处搜出了一串佛珠,上面刻了 24 个英文单词,那就是你的 private key(BIP39 助记词)……据说有价值 1.3 亿美元的比特币(我只是引用传闻)。」


三、操作系统安全:访问控制

(参考时间: 00:24)

操作系统就是一组 API + 好多对象;程序在运行时由进程/线程承载,通过 API 访问对象。

地址空间隔离:你的进程被封在一个虚拟地址空间里;对操作系统对象的所有访问都必须通过 API——「操作系统牢牢抓住了这个口子」。

API 有两个方面:行为(fork/mmap/read/write…)和访问控制——「当应用调用 open 时,操作系统知道 open 的行为,但还可以有权拒绝这次访问」:

实现 Confidentiality 和 Integrity:进程 + 虚拟内存实现隔离,所有共享对象访问都通过系统调用

📷 实现 Confidentiality 和 Integrity:进程 + 虚拟内存实现隔离,所有共享对象访问都通过系统调用
回到原视频 (00:25:00)

访问控制矩阵:第一列是进程,第二列是对象,然后是访问方式(读/写/更细的行为)——「这是一个巨大无比的表,你肯定不能直接存储它」:

$$\text{几万个进程} \times \text{几百万个对象} \times \text{多种访问方式}$$

编者拓展|「这是一个可以 low-rank decomposition 的矩阵」:一个低权限用户,「除了自己的东西和系统共用的部分,几乎什么都该拒绝」——所以信息量很少。UNIX 的做法是用整数表示身份:把矩阵拆成几个关键要素。

访问控制矩阵:进程 × 对象 × 访问方式,用「身份 + 权限」做低秩分解

📷 访问控制矩阵:进程 × 对象 × 访问方式,用「身份 + 权限」做低秩分解
回到原视频 (00:28:30)

UNIX 的模型:用户(UID)+ 组(GID)+ 权限(mode):

`/etc/passwd`:用户名、UID、GID、comment、home、login shell——密码存 hash

📷 `/etc/passwd`:用户名、UID、GID、comment、home、login shell——密码存 hash
回到原视频 (00:32:00)

编者拓展|setuid:一个危险的 legacy 机制:为什么普通用户能改自己的密码(而 /etc/passwd 不能随便改)?——因为 /bin/passwd 设置了 setuid 位(rws),执行时以文件所有者(root)的权限运行。「ls -l 里这个文件是红色的,意味着它超越了执行者本身的权限——一旦 passwd 有漏洞,就很可能被攻击。」

讲师推荐了一篇 2003 年的论文 Setuid Demystified:「它把 real uid / effective uid / saved uid / filesystem uid 讲清楚了。」——「现在的论文都爱叫 xx Demystified,我很讨厌这个标题——本来就不是 mystery。」

setuid:`/bin/passwd` 执行时以 root 权限运行(`ls -l` 里显示为红色)

📷 setuid:`/bin/passwd` 执行时以 root 权限运行(`ls -l` 里显示为红色)
回到原视频 (00:38:00)

更细粒度的访问控制:

AppArmor / eBPF(seccomp):把访问控制做进内核,给程序「划一个虚拟的世界」

📷 AppArmor / eBPF(seccomp):把访问控制做进内核,给程序「划一个虚拟的世界」
回到原视频 (00:45:30)


四、访问控制还不够

4.1 Buffer Overflow

(参考时间: 00:48)

「你永远无法预期你的用户会怎么样恶意地使用你的产品。」

void vulnerable() {
    char buffer[64];
    scanf("%s", buffer);      // 未检查长度!
    printf("%s\n", buffer);   // 像 echo 一样
}

buffer overflow 是 undefined behavior——它从低地址往高地址写,会覆盖返回地址:

Buffer overflow:从低地址往高地址写,覆盖保存的返回地址

📷 Buffer overflow:从低地址往高地址写,覆盖保存的返回地址
回到原视频 (00:52:30)

编者拓展|Smashing the Stack for Fun and Profit(1996):这篇著名论文「开启了全新的世界」。攻击流程(x86-64 下的经典 stack smashing):先知道 buffer 的地址 → 发送一个精心构造的、没有 \0(因为 strcpy 碰到 \0 就停)的 payload → 把返回地址跳到栈上的 shellcode → execve("/bin/sh")。

防御:

Stack Protector(Canary):用「金丝雀」在返回前检测栈是否被破坏

📷 Stack Protector(Canary):用「金丝雀」在返回前检测栈是否被破坏
回到原视频 (00:59:00)

编者说明:「攻和防总是互相成长的——有更强的攻击就有更强的防御。」「如果你不在意开销、愿意接受程序慢一点(尤其在 AI 时代),安全问题可能会好解决不少。」

4.2 时间侧信道与 Meltdown

(参考时间: 01:02)

一个看起来完全没问题的内核函数:

int check_password(const char *given) {
    for (int i = 0; ; i++) {
        if (quit_pass[i] != given[i]) { sleep(3); return DENIED; }
        if (given[i] == 0) break;
    }
    return OK;
}

攻击:你把用户名密码「猜的那一位」卡在一个内存页的边界上,然后想办法让这一页换出内存(申请大量内存 + 制造内存压力,让内核的 kcompactd 把冷页压缩/换出):

「只要多试几次,就可以一位一位把它猜出来——在多项式时间内,而不是指数时间。」

Timing attack:把密码卡在页边界,靠「换页的时间差」逐位猜出密码

📷 Timing attack:把密码卡在页边界,靠「换页的时间差」逐位猜出密码
回到原视频 (01:06:30)

编者拓展|Meltdown(2018):这与上面的 timing attack 很像。攻击者让分支预测器错误地认为某些代码会执行,于是那些代码被投机执行——但 load 时 CPU 没有检查权限(CPU 的 bug),把内核内存的一个字节读进寄存器;随后这些指令被销毁(没有架构上的副作用),但对 cache 的影响保留了下来。另一个线程去探测 index × 4096 的访问时间——在某个下标处明显快(cache hit),于是「偷出了一个你永远没有权限访问的字节」。

「你想都不敢想,它是 CPU 设计者好多个机制共同加起来导致的结果。」暴露后,操作系统内核再也不敢把内核和进程映射进同一地址空间了。「但这只是一个开始——漏洞是无穷无尽的,甚至你都看不见你的对手在哪。」

Meltdown:投机执行 + cache 侧信道,偷出「你永远没有权限访问」的一个字节

📷 Meltdown:投机执行 + cache 侧信道,偷出「你永远没有权限访问」的一个字节
回到原视频 (01:09:30)

编者拓展|物理侧信道:历史上还有更脑洞大开的工作—— - 放一个天线,隔着三层石膏板、10 米距离偷出 CRT 显示器的图像(电子束穿过墙壁); - 通过 CPU 的电磁信号偷出 private key——「藏在一个饼里放在你旁边,你 SSH 登录时就把 private key 偷走了」; - 液氮冷启动:你离开教室的瞬间拆开笔记本、液氮一浇,把内存条放进保温箱——「你的 DRAM 里还有你的 Word 文档、浏览器 session、private key……」


五、AI 时代的安全

(参考时间: 01:14)

供应链攻击:恶意的 `postinstall` hook 遍历 home 目录与环境变量,偷走 API key

📷 供应链攻击:恶意的 `postinstall` hook 遍历 home 目录与环境变量,偷走 API key
回到原视频 (01:21:00)

奶奶漏洞 / prompt injection:早期模型的越狱方式,如今越来越难但理论上仍有空间

📷 奶奶漏洞 / prompt injection:早期模型的越狱方式,如今越来越难但理论上仍有空间
回到原视频 (01:15:00)

编者拓展|「没有绝对的防御」:讲师指出——「你只能通过沙箱或访问控制,给一次大模型调用最小的权限。」但这与「龙虾(Claw/agent)的成功」相矛盾:「你把所有权限给它,它才好用;你限制它,它就老说『这个权限没有』。」——这是一个新的安全问题。


总结

编者说明:本讲与下一讲(容器、虚拟化、Agent 时代的沙箱)都是补充内容,不考试——但它们与当下每一个人都高度相关。


附:官方参考与延伸阅读

本讲的讲义见课程主页,以下链接按书中出现顺序整理:

主题 链接
Smashing the Stack for Fun and Profit(Phrack 49, 1996) http://phrack.org/issues/49/14.html
Setuid Demystified(USENIX Security '02) https://www.usenix.org/legacy/events/sec02/full_papers/chen/chen.pdf
Meltdown 论文 https://meltdownattack.com/
常见漏洞数据库(CVE) https://cve.mitre.org/
xz-utils 后门(CVE-2024-3094) https://www.openwall.com/lists/oss-security/2024/03/29/4
NIST 密码学标准(SHA-3 等) https://csrc.nist.gov/projects/hash-functions
OSTEP(第 53–55 章:Security / Authentication / Access Control) https://pages.cs.wisc.edu/~remzi/OSTEP/

阅读材料:读 Smashing the Stack for Fun and Profit 与 Setuid Demystified;了解 Meltdown/Spectre;关注供应链攻击案例。

版权说明:本讲讲义与幻灯片作者为蒋炎岩,采用 Creative Commons BY-NC 4.0 许可,来源于 https://jyywiki.cn/OS/2026/。视频画面与字幕版权归原作者所有。本书为课堂内容的学习性重构。