7.5. vfork(2) 사용을 피해라

유닉스 계열 시스템에서 새로운 프로세스를 생성하는 이식성 있는 방법은 fork(2) 호출을 사용하는 것이다. BSD 는 최적화 기법으로 vfork(2) 라는 변형을 도입하였는데 vfork(2) 에서는 fork(2) 와는 달리 자식은 execve(2V) 또는 exit 호출이 일어날 때까지 부모의 제어 메모리와 쓰레드를 빌린다; 부모 프로세스는 일시 정지된 반면 자식은 그 자원들을 사용하고 있다. 예전 BSD 시스템에서 fork(2) 가 실제로 메모리가 복사되도록 할 수 있는 반면 vfork(2) 는 그렇지 않을 것이다라는 것이 근본적 이유였다. 리눅스는 전혀 이러한 문제가 없었다; 리눅스는 copy-on-write (write 하는 동안 복사) 의미 체계를 내부적으로 사용했기 때문에 페이지들이 변경되었을 때만 단지 이들을 복사한다 (실제로 복사되어야 하는 어떤 테이블이 있는데 대부분 상황에서 이들의 오버헤드가 큰편은 아니다). 그럼에도 불구하고 어떤 프로그램들은 vfork(2) 에 의존하기 때문에 최근 리눅스는 BSD vfork(2) 의미 체계를 구현하였다 (이전 vfork(2) 는 fork(2) 의 alias 였다).

vfork(2) 와 관련해서는 많은 문제가 있다. 이식성 관점에서 보았을 대 vfork(2) 와 관련된 문제는 프로세스가 그 부모를 간섭하지 않는 것이 특히 고수준 언어에서 실제로 매우 어렵다는 것이다. 간섭하지 않는다는 요건은 실제 생성된 머신 코드에 적용되며 많은 컴파일러들은 의도되지 않은 간섭을 일으키는 숨겨진 temporaries 와 다른 코드 구조들을 생성한다. 결과: vfork(2) 를 사용하는 프로그램들은 코드가 변경될 때 또는 컴파일러 버전이 변경될 때 더욱 실패하기 쉬울 것이다.

보안적인 프로그램의 경우 리눅스 (적어도 2.2.17 까지의 2.2 버전들) 는 vfork() 구현에 있어 경쟁 상태에 취약하기 때문에 리눅스 시스템에서는 더욱 나쁘다. 권한을 갖는 프로세스가 사용자 명령들을 실행시키기 위해 리눅스에서 vfork(2)/execve(2) 쌍을 사용한다면 자식 프로세스가 사용자의 UID 로 이미 작동하고 있지만 아직 execve(2) 로 들어가지 않는 동안 경쟁 상태가 존재한다. 사용자는 이 프로세스에 SIGSTOP 를 포함한 시그널들을 보낼 수 있을지도 모른다. vfork(2) 의 의미 체계때문에 권한을 갖는 부모 프로세스도 또한 블록당할 수 있다. 그 결과 권한이 없는 프로세스가 권한을 갖는 프로세스에게 정지하도록 시킬 수 있으며 권한을 갖는 프로세스의 서비스에 대해 서비스 부인 공격을 초래할 것이다. FreeBSD 와 OpenBSD 는 적어도 이런 경우를 명확하게 다루는 코드를 갖고 있으며 따라서 저자가 알기에 이러한 문제에 취약하지 않다. 리눅스에서 이 문제를 언급하고 "security-audit" 메일링 리스트에 2000년 9월에 문서화한 한 Solar Designer 에 감사한다.

vfork(2) 에 대한 최종 결과는 간단하다: 프로그램에서 vfork(2) 를 사용하지 마라. 이는 어렵지 않아야 한다; vfork(2) 의 기본적인 사용은 vfork 의 의미 체계를 필요로 하는 예전 프로그램을 지원하는 것이다.