assert를 비활성화하면 불가능하다고 가정한 조건이 실제로 발생해도 프로그램이 계속 실행됨
프로그램은 틀린 가정 아래 계속 실행되고, 이는 unchecked illegal behavior가 아니더라도 오동작의 한 형태임
unchecked illegal behavior나 C의 undefined behavior가 위험한 이유는 프로그램을 weird machine으로 바꾸는 경로가 될 수 있기 때문임
충분히 복잡한 소프트웨어에서는 UIB가 없어도 프로그램이 의도하지 않은 방식으로 비틀릴 수 있음
런타임에 assert가 거짓이 되는 것은 명세에서 벗어나는 일이며, 그 자체로 의도하지 않은 작업을 수행하게 만들 수 있음
SQL injection은 UIB 없이도 weird-machine급 오동작을 일으키는 구체적이고 널리 퍼진 예임
프로그램 오동작 비용이 너무 높다면 assert를 켜 두는 편이 맞음
성능이 매우 중요해 오동작 위험을 감수할 수 있다면 assert를 최적화 기회로 쓰는 편이 맞음
assert를 비활성화하면 성능을 놓치면서도 실제보다 더 안전하다고 착각하기 쉬움
잘못된 assert가 코드베이스를 속이는 방식
핵심 위험은 틀린 assert가 테스트에서는 드러나지 않고 프로덕션에서만 실패할 수 있다는 데 있음
모든 assert가 항상 참이라고 보장할 수 있다면 assert를 최적화에 쓰는 것은 논란이 되지 않음
테스트가 모든 잘못된 assert를 잡는다고 보장할 수 있다면 프로덕션 최적화도 안전해짐
실제로는 잘못된 assert를 작성할 수 있고, 테스트가 반드시 잡아주지도 않음
assert를 프로덕션에서 끄면 잘못된 assert를 최대한 빨리 발견할 기회를 잃음
더 심각한 문제는 이후 코드가 그 잘못된 assert에 의존해 계속 작성된다는 데 있음
예시 코드에서는 processThing이 이미 시작된 thing에서만 호출되어야 한다는 가정을 assert로 둠
fn processThing(thing: Thing) void {
// this function must always be invoked on
// a thing that has already been started
assert(thing.is_started);
// ...
}
이 assert가 테스트에서는 실패하지 않고, 프로덕션에서는 비활성화되어 실제로 거짓이 될 수 있다는 사실을 놓칠 수 있음
사용자에게 관찰되는 오동작이 없으면 문제가 없는 것처럼 보이고 개발이 계속됨
이후 누군가 thing이 이미 시작되었으므로 추가 준비 없이 baz를 호출해도 된다고 보고 코드를 추가할 수 있음
fn processThing(thing: Thing) void {
// this function must always be invoked on
// a thing that has already been started
assert(thing.is_started);
// ...
// Since thing is already started, we don't
// need to foo the bar before bazzing the qux.
// It would be really bad to baz the qux otherwise,
// so we add an assert for good measure.
assert(thing.is_fooed);
thing.baz(qux);
}
두 번째 assert 자체가 논리적으로 맞더라도, 첫 번째 assert가 실제로는 거짓이 될 수 있다면 위험이 생김
테스트에서는 첫 번째 assert가 실패하지 않으므로 두 번째 assert도 실패하지 않음
프로덕션에서는 assert가 비활성화되어 취약점이 코드베이스에 들어오는 순간을 알아차리지 못할 수 있음
코드 안의 assert가 개발자를 속이는 상태라면 올바른 코드를 작성하는 일이 불합리하게 어려워짐
선택지는 프로그램의 우선순위에 따라 달라짐
프로그램마다 우선순위가 다르며, 어떤 프로그램은 오동작 위험 최소화보다 성능을 우선하는 것이 정당할 수 있음
이 경우 assert를 최적화 기회로 바꾸는 선택은 자연스러움
프로덕션에서 assert를 관성적으로 비활성화하는 것은 assert를 켜 두는 것보다도, 성능 최적화를 적극 활용하는 것보다도 열등한 선택으로 평가됨
ReleaseFast에 대해 매우비판적이면서 assert 비활성화는 무비판적으로 받아들이는 태도는 모순적임