[Rejected] PEP 330 - Python Bytecode Verification
원문 링크: PEP 330 - Python Bytecode Verification
상태: Rejected 유형: Standards Track 작성일: 17-Jun-2004
PEP 330 – Python 바이트코드 검증 (Python Bytecode Verification)
요약 (Abstract)
Python Virtual Machine (PVM) 바이트코드가 “잘 구성되지 않은(well-formed)” 경우, 값 스택(value stack)의 언더/오버플로우(under/overflow) 또는 PVM 프로그램 공간의 임의 영역 읽기/쓰기 등 다양한 오류를 유발하여 PVM을 충돌시키거나 악용할 수 있습니다. 대부분의 이러한 유형의 오류는 PVM 바이트코드가 실행 전에 일련의 간단한 제약 조건을 위반하지 않는지 확인함으로써 제거될 수 있습니다.
이 PEP는 Python Virtual Machine (PVM) 바이트코드의 형식 및 구조에 대한 일련의 제약 조건을 제안하고, 이 검증 프로세스를 Python으로 구현한 것을 제공합니다.
선언 (Pronouncement)
Guido (반 로섬)는 검증 도구가 어느 정도 가치가 있다고 생각합니다. 만약 누군가 Tools/scripts
에 이를 추가하려 한다면, PEP는 필요하지 않습니다.
이러한 도구는 “바이트코드 해킹(bytecodehacks)” 또는 .pyc
파일의 직접 편집으로 인한 결과물을 검증하는 데 가치가 있을 수 있습니다. 보안 조치로서 그 가치는 다소 제한적입니다. 왜냐하면 완벽하게 유효한 바이트코드도 여전히 끔찍한 일을 할 수 있기 때문입니다. 제한된 실행(restricted execution) 개념이 성공적으로 부활한다면 이러한 상황은 바뀔 수 있습니다.
동기 (Motivation)
Python Virtual Machine은 Python 언어에서 바이트코드 표현으로 컴파일된 Python 프로그램을 실행합니다. PVM은 실행되는 모든 바이트코드가 수많은 암묵적인 제약 조건에 대해 “잘 구성되어(well-formed)” 있다고 가정합니다. 이러한 제약 조건 중 일부는 런타임에 확인되지만, 대부분은 생성되는 오버헤드 때문에 확인되지 않습니다.
디버그 모드에서 실행할 때 PVM은 특정 바이트코드가 이러한 제약 조건을 위반할 수 없도록 여러 런타임 검사를 수행하여 인터프리터의 충돌이나 악용을 어느 정도 방지합니다. 이러한 검사는 인터프리터에 측정 가능한 오버헤드를 추가하며, 일반적으로 일반적인 사용에서는 비활성화됩니다.
잘못 구성된(ill-formed) 바이트코드가 디버그 모드에서 실행되지 않는 PVM에 의해 실행되면 다양한 치명적 및 비치명적 오류를 유발할 수 있습니다. 일반적으로 잘못 구성된 코드는 PVM이 seg-fault
를 발생시키고 OS가 인터프리터를 즉시 강제 종료하게 합니다.
잘못 구성된 바이트코드는 잠재적으로 인터프리터를 악용하여 Python 바이트코드가 임의의 C-레벨 머신 명령어를 실행하거나 인터프리터의 비공개 내부 데이터 구조를 수정할 수 있게 할 수 있습니다. 이를 교묘하게 사용하면 애플리케이션이 객체에 적용하려는 모든 형태의 보안 정책을 무력화할 수 있습니다.
실제적으로 악의적인 사용자가 악용 목적으로 PVM에 유효하지 않은 바이트코드를 “주입”하기는 어렵지만 불가능하지는 않습니다. 버퍼 오버플로우(Buffer overflow) 및 메모리 덮어쓰기(memory overwrite) 공격은 일반적으로 이해되며, 특히 악용 페이로드가 네트워크를 통해 암호화되지 않은 상태로 전송되거나 파일 또는 네트워크 보안 권한 취약점이 추가 공격의 발판으로 사용될 때 더욱 그렇습니다.
이상적으로는 악의적으로 조작되었는지 여부에 관계없이 바이트코드가 PVM의 작동을 방해하기 위해 기본 C-레벨 데이터 구조를 읽거나 쓰도록 허용되어서는 안 됩니다. 간단한 사전 실행 검증 단계는 바이트코드가 런타임에 값 스택을 오버플로우/언더플로우시키거나 PVM 프로그램 공간의 다른 민감한 영역에 접근할 수 없도록 보장할 수 있습니다.
이 PEP는 PVM에 의해 실행되기 전에 Python 바이트코드에 대해 수행되어야 하는 여러 유효성 검사 단계를 제안하여, 명령 및 해당 피연산자에 대한 정적(static) 및 구조적(structure) 제약 조건을 준수하도록 합니다. 이러한 단계는 간단하며 충돌을 유발할 수 있는 많은 유형의 유효하지 않은 바이트코드를 포착합니다. 또한 검증 패스를 통해 일부 런타임 검사를 미리 제거할 가능성도 있습니다.
물론 “완전히 안전한(completely safe)” 바이트코드를 모든 정의에서 검증할 방법은 없습니다. 바이트코드 검증이 있더라도 Python 프로그램은 다양한 이유로 seg-fault
를 발생시킬 수 있으며 앞으로도 계속 발생할 가능성이 높고, 치명적이든 아니든 다양한 유형의 런타임 오류를 유발할 것입니다. 여기서 제안된 검증 단계는 바이트코드 수준에서 광범위한 치명적이고 미묘한 오류를 유발할 수 있는 쉬운 허점을 단순히 막는 것입니다.
현재 Java Virtual Machine (JVM)은 여기서 제안된 방식과 매우 유사한 방식으로 Java 바이트코드를 검증합니다. 따라서 JVM Specification 버전 2의 섹션 4.8과 4.9는 아래에 설명된 일부 제약 조건의 기초로 사용되었습니다. 모든 Python 바이트코드 검증 구현은 최소한 이러한 제약 조건을 적용해야 하지만, 여기에 국한되지 않을 수 있습니다.
바이트코드 명령어에 대한 정적 제약 조건 (Static Constraints on Bytecode Instructions)
- 바이트코드 문자열은 비어 있으면 안 됩니다 (
len(co_code) > 0
). - 바이트코드 문자열은 최대 크기를 초과할 수 없습니다 (
len(co_code) < sizeof(unsigned char) - 1
). - 바이트코드 문자열의 첫 번째 명령어는 인덱스 0에서 시작합니다.
- 올바른 수의 피연산자를 가진 유효한 바이트코드만 바이트코드 문자열에 포함될 수 있습니다.
바이트코드 명령어 피연산자에 대한 정적 제약 조건 (Static Constraints on Bytecode Instruction Operands)
- 점프 명령어의 대상은 코드 경계 내에 있어야 하며, 명령어 사이에 있어서는 안 되고 항상 명령어에 위치해야 합니다.
LOAD_*
명령어의 피연산자는 해당 데이터 구조에 대한 유효한 인덱스여야 합니다.STORE_*
명령어의 피연산자는 해당 데이터 구조에 대한 유효한 인덱스여야 합니다.
바이트코드 명령어 간의 구조적 제약 조건 (Structural Constraints between Bytecode Instructions)
- 각 명령어는 해당 호출로 이어지는 실행 경로와 관계없이 값 스택에 적절한 수의 인자와 함께 실행되어야 합니다.
- 명령어가 여러 다른 실행 경로를 따라 실행될 수 있는 경우, 해당 경로와 관계없이 명령어 실행 전에 값 스택은 동일한 깊이를 가져야 합니다.
- 실행 중 어떤 시점에서도 값 스택은
co_stacksize
가 암시하는 깊이보다 커질 수 없습니다. - 실행은
co_code
의 바닥을 벗어나지 않습니다.
구현 (Implementation)
이 PEP는 Python으로 작성된 Python 바이트코드 검증 구현을 위한 작업 문서입니다. 이 구현은 바이트코드를 실행하기 전에 PVM에 의해 암묵적으로 사용되지 않지만, 다음 스니펫과 같이 유효하지 않은 바이트코드에 대해 우려하는 사용자가 명시적으로 사용하도록 고안되었습니다.
import verify
verify.verify(object)
verify
모듈은 dis.dis
와 동일한 종류의 인자(클래스, 메서드, 함수 또는 코드 객체)를 받는 verify
함수를 제공합니다. 이 함수는 객체의 바이트코드가 이 PEP의 사양에 따라 잘 구성되어 있는지 확인합니다.
코드가 잘 구성된 경우 verify
호출은 오류 없이 조용히 반환됩니다. 오류가 발생하면 실패 원인을 나타내는 인자를 가진 VerificationError
를 발생시킵니다. 오류를 어떤 식으로든 처리하거나 유효하지 않은 코드를 실행할지 여부는 프로그래머에게 달려 있습니다.
Phillip Eby는 참조 구현에서 사용되는 바이트코드 스택 깊이 검증을 위한 의사 코드(pseudo-code) 알고리즘을 제안했습니다.
검증 문제 (Verification Issues)
이 PEP는 소수의 검증만을 설명합니다. 논의와 분석을 통해 더 많은 검증이 필요하겠지만, 미래의 검증이나 커스텀, 프로젝트별 검증이 필요할 가능성이 매우 높습니다. 이러한 이유로, 미래의 검증기를 등록하기 위해 테스트 구현에 검증 등록 인터페이스를 추가하는 것이 바람직할 수 있습니다. 커스텀 검증기는 현재 구현을 서브클래싱하고 확장하여 추가 동작을 구현할 수 있으므로, 이에 대한 필요성은 미미합니다.
필요한 변경 사항 (Required Changes)
Armin Rigo는 스택 효과를 정적으로 분석하기 위해 여러 바이트코드 수정이 필요할 것이라고 언급했습니다. 이들은 END_FINALLY
, POP_BLOCK
, MAKE_CLOSURE
입니다. Armin과 Guido는 이미 이 명령어들을 수정하는 방법에 대해 동의했습니다. 현재 Python 구현은 이 명령어들에 대해서는 회피(punts)하고 있습니다.
이 PEP는 검증 단계를 인터프리터에 추가할 것을 제안하지 않으며, 단지 선택적 사용을 위해 표준 라이브러리에 Python 구현을 제공할 뿐입니다. 이 검증 절차가 C로 번역되거나 PVM에 포함되거나 어떤 방식으로든 강제될지 여부는 향후 논의로 남겨져 있습니다.
참조 (References)
The Java Virtual Machine Specification 2nd Edition
http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html
저작권 (Copyright)
이 문서는 퍼블릭 도메인에 공개되었습니다.
⚠️ 알림: 이 문서는 AI를 활용하여 번역되었으며, 기술적 정확성을 보장하지 않습니다. 정확한 내용은 반드시 원문을 확인하시기 바랍니다.
Comments