title: "Let’s Encrypt证书吊销事故的思考"
date: 2020-03-28T00:11:54+08:00
description: "如何保障代码的安全性?"
featured_image: ""
categories: "其它"
tags: []
这个事故的原因上文已经说得很清楚了,这里摘抄一下:
func main() {
var out []*int
for i := 0; i < 3; i++ {
out = append(out, &i)
}
fmt.Println("Values:", *out[0], *out[1], *out[2])
fmt.Println("Addresses:", out[0], out[1], out[2])
}
Values: 3 3 3
Addresses: 0x40e020 0x40e020 0x40e020
Code review人类容易犯错的本质和高昂的测试成本之间的矛盾
人类容易犯错的本质
制造错误本质就是生物界的一种合理现象,如果没有原始细菌DNA复制过程产生错误,原始细菌始终都只会分裂成原始细菌,而不会变异成多细胞生物、小型动物、大型动物,人类也不可能诞生。没有程序员敢打报票说自己编写的系统永远不存在BUG,即使你是google、微软资深的工程师。况且人的状态不是永远都保持良好的,即使你平时头脑非常灵敏可靠,但不排除在长时间加班或喝醉酒的情况下,无意给自己的系统留下几个难以排查的BUG。
高昂的测试成本
每一个系统都希望测试覆盖率能够达到100%,虽然说是可以做到的,但是消耗的时间和人力是非常大的,一般来说写单元测试的时间是逻辑实现代码的3到4倍。而且即使覆盖率到达100%,也不能保证系统没有BUG:也存在某些情况是测试用例没有覆盖到的。
Code reviewLet’s Encrypt证书吊销事故
如何避免?
既然上面的观点貌似都否认了人为避免错误发生的可靠性,那怎么去避免这种故障呢?答案是靠计算机。计算机相比人来说,要可靠得多的,可以长时间进行大量运算而不会出错,相反人类很长时间工作很容易会疲劳产生不确定性。
rustc
初学者往往觉得写Rust代码非常难编译通过,而产生了沮丧心理,甚至抗拒、愤怒和谩骂。正如我们面对复杂的交通秩序,有时候也会产生抗拒心理,但其实这些都是以往的各种交通事故,多少人付出了鲜血和生命的代价才积累下来的,所以我们应该去敬畏它,遵守它。
上面的代码尝试用Rust来重写:
fn main() {
let mut out = [&0usize; 3];
for i in 0..out.len() {
out[i] = &i;
}
}
编译不通过:
error[E0597]: `i` does not live long enough
--> src/main.rs:4:18
|
4 | out[i] = &i;
| ------ ^^ borrowed value does not live long enough
| |
| borrow later used here
5 | }
| - `i` dropped here while still borrowed
error: aborting due to previous error
或者:
fn main() {
let mut out = [&mut 0usize; 3];
for i in 0..out.len() {
out[i] = &mut i;
}
}
编译也不通过:
error[E0277]: the trait bound `&mut usize: std::marker::Copy` is not satisfied
--> src/main.rs:2:19
|
2 | let mut out = [&mut 0usize; 3];
| ^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `&mut usize`
|
= help: the following implementations were found:
<usize as std::marker::Copy>
= note: `std::marker::Copy` is implemented for `&usize`, but not for `&mut usize`
= note: the `Copy` trait is required because the repeated element will be copied
事实上用safe Rust是编写不出来的,因为压根编译不过。这就体现了Rust的安全性,并不只有内存安全和并发安全,还有很多防止低级错误的特性。