원문 캡처 · github.com
원문 캡처 · github.com

cppC는 기존 C++ 코드에 메모리 안전성 분석을 더하는 컴파일러다. delete 후 사용, 이중 해제, 수명이 끝난 지역 변수의 주소 반환, 상수 범위 초과 인덱스를 기본 설정으로 빌드 중 탐지한다.

기능과 한계

cppC는 main.cpp, main.cc, main.cxx, .c 형식의 일반 C++ 코드를 처리한다. g++처럼 사용할 수 있고 같은 헤더와 표준 라이브러리를 사용해 바이너리를 만든다. 각 소스 파일은 표준 빌드 과정에 앞서 cppC의 메모리 안전성 검사기를 거친다. g++는 전처리와 코드 생성을 맡고, 안전성 검사는 cppC 자체 코드로 수행한다. 제작자 설명에 따르면 검사기는 토큰 단위 별칭 그룹 분석을 사용하며, 이전 차용 검사기 설계에서 영감을 얻었다.

cppC는 별칭을 통해 접근하는 해제된 메모리 사용과 이중 delete를 찾고, 지역 변수 주소의 반환과 상수 인덱스의 범위 초과도 보고한다. 기본 설정에서는 발견 사항이 경고로 표시되고 빌드는 계속된다. --strict-safety를 지정하면 오류로 처리해 빌드를 중단한다. 안전성 검사만 실행하려면 --check, 검사를 건너뛰려면 --no-safety를 쓴다.

항목일반 g++cppC
C++ 컴파일가능가능(같은 출력 속도 등급)
빌드 시 해제 후 사용 탐지특별한 플래그로 빌드할 때만기본값으로 탐지
이중 delete보고하지 않음탐지
수명이 끝난 지역 변수 반환(return &x;)경고가 묻힐 수 있음이름이 명시된 발견 사항으로 보고
상수 범위 초과 인덱스놓치는 경우가 많음탐지
보고 형식컴파일러 전문 용어오류, 변수, 해제된 줄을 한 줄로 표시
--strict-safety해당 없음발견 사항이 빌드를 중단

검사기는 표준 라이브러리 코드가 아니라 사용자 코드만 분석한다. 실제 C++ 전처리는 g++ -E에 맡기므로 #include 연결, #define, #ifdef, 시스템 헤더와 기존 빌드 환경을 사용할 수 있다. 바이너리 생성도 시스템 g++가 담당한다. 제작자는 그 결과 바이너리가 컴파일러 도구 체인의 정확성을 따르며 cppC의 검사 결과가 추가된다고 설명한다.

설치와 실행

저장소의 빠른 시작 절차는 다음과 같다. make는 g++만 필요로 하며 bin/cppc를 빌드하고, make test는 메모리 안전성 테스트 묶음을 실행한다.

text
git clone <repo>
cd cppC
make # builds bin/cppc (needs just g++)
make test # full memory-safety test suite

프로그램을 빌드하고 바로 실행하는 방식은 다음과 같다.

text
bin/cppc hello.cpp # builds and runs immediately
bin/cppc main.cpp # cached: second run is instant (see below)

바이너리를 따로 만들 때는 --build 또는 -o를 사용하고, 분석만 실행할 때는 --check를 지정한다.

text
bin/cppc --build main.cpp # produces ./main
bin/cppc main.cpp -o app # named output
bin/cppc --check main.cpp # safety analysis only, no binary

실행 모드는 빌드한 바이너리를 기본적으로 ~/.cache/cppc에 저장한다. CPPC_CACHE 환경 변수로 캐시 위치를 바꿀 수 있다. 다음에 같은 소스를 실행하면 캐시된 바이너리를 쓰고, 소스가 바뀌었을 때만 다시 컴파일한다. CPPC_VERBOSE=1을 지정하면 캐시 사용 로그를 확인할 수 있다.

text
$ CPPC_VERBOSE=1 bin/cppc hello.cpp cppc: [cached] /home/you/.cache/cppc/41d887c86ce4c2c1.bin

--no-cache를 쓰면 매번 새로 빌드하고 실행한다.

탐지 예시와 테스트

다음은 저장소의 tests/에 있는 예시다. 포인터 q가 p와 같은 할당 영역을 가리킨 뒤 p를 해제하면, cppC는 별칭인 q를 통한 사용도 보고한다.

cpp
int main() {
 int* p = new int;
 int* q = p; // q aliases p
 delete p;
 return *q; // cppc: warning: use-after-delete of 'q' (deleted at line 4)
}

검사기는 같은 할당 영역을 가리키는 포인터를 합집합-찾기(union-find) 별칭 그룹으로 묶는다. 하나를 해제하면 모든 별칭을 사용할 수 없는 상태로 표시한다.

같은 포인터를 두 번 해제하는 경우에는 첫 해제 줄을 함께 보고한다.

cpp
int main() {
 int* p = new int;
 delete p;
 delete p; // cppc: warning: double delete of 'p' (first deleted at line 3)
 return 0;
}

수명이 끝나는 지역 변수의 주소를 반환하는 코드도 탐지한다.

cpp
int* bad() {
 int x = 5;
 return &x; // cppc: warning: returning address of local 'x'
}

배열 크기를 벗어난 상수 인덱스도 보고한다.

cpp
int main() {
 int a[4];
 a[5] = 1; // cppc: warning: index 5 out of bounds for 'a' (size 4)
 return 0;
}

기본 설정에서는 발견 사항이 경고로 남고 빌드는 계속된다. --strict-safety를 추가하면 발견 사항이 오류가 되어 빌드를 중단한다. 다음은 사용 후 해제를 오류로 처리하는 로그 예시다.

text
$ bin/cppc --strict-safety bad.cpp cppc: bad.cpp:5: error: use-after-delete of 'p' (deleted at line 4) cppc: 1 memory-safety finding(s): not compiling (--strict-safety)

제작자는 기본 모드를 발견 사항을 관찰하는 용도로, --strict-safety 모드를 강제 적용하는 용도로 설명하며 CI에서도 사용할 수 있다고 말한다. 테스트는 make test로 실행한다. 저장소에 적힌 결과에는 정상 프로그램 실행과 사용 후 해제, 별칭을 통한 사용 후 해제, 이중 해제, 지역 변수 주소 반환, 범위 초과 탐지 테스트의 통과가 포함된다. 같은 발견 사항은 --strict-safety에서 빌드를 중단하며, 전체 결과는 ALL TESTS PASSED로 표시된다.

text
PASS: tests/ok_std.cpp runs PASS: tests/ok_heap.cpp runs PASS: tests/uaf_warn.cpp warns 'use-after-delete' PASS: tests/uaf_alias_warn.cpp warns 'use-after-delete' PASS: tests/dblfree_warn.cpp warns 'double delete' PASS: tests/retlocal_warn.cpp warns 'returning address of local' PASS: tests/oob_warn.cpp warns 'out of bounds' ... (same findings abort the build under --strict-safety) ALL TESTS PASSED

저장소는 현재 탐지 가능한 네 종류가 테스트에 포함돼 있고, V8 소스에 적용해 거짓 양성 0건으로 검증했다고 주장한다. 이 결과는 저장소가 밝힌 내용이다.

현재 범위와 향후 계획

현재 상태는 v0.2.1이다. 로드맵에는 v0.2에서 C++ 컴파일(cpp, cc, cxx)과 메모리 안전성 분석, 실행·빌드 모드를 추가했다고 적혀 있다. v0.2.1에서는 --std 플래그를 추가해 V8 같은 C++20 프로젝트를 지원하고 분석 전용 --check를 수정했다.

아직 탐지하지 못하는 항목과 저장소에 적힌 이유, 계획은 다음과 같다.

버그 유형어려운 이유계획
메모리 누수(delete 누락)사용 추적만으로는 부족하고 소유권 규칙이 필요다음 단계에서 추론 추가
변수 인덱스 범위 초과(a[i], i를 모르는 경우)값 추적 또는 실행 중 검사 삽입이 필요실행 중 검사
널 역참조분기별 흐름 분석이 필요흐름 분석 단계
함수 간 버그(p가 다른 번역 단위에서 해제됨)함수 간 분석이 필요전체 프로그램 분석 단계
분기 민감 버그(실행되지 않은 분기 안의 delete)경로별 분석은 경우의 수가 빠르게 늘어남경로 민감 분석 단계
C 스타일 malloc/free 버그별칭 모델은 같지만 다른 API 계열임추적기 확장
실행 중 트랩(정적 분석이 놓치는 경우를 새니타이저처럼 탐지)생성된 바이너리에 검사 삽입이 필요계측 단계

저장소는 탐지 범위를 과장하는 컴파일러보다 보장하는 범위를 정확히 알리는 컴파일러가 낫다며 이 한계 목록을 공개한다고 설명한다. 로드맵에는 생성된 C++에 범위·널 검사 삽입, 함수 간 별칭 분석, 반복자 무효화 및 컨테이너 수명 진단, 종료 코드 기준과 SARIF 출력을 갖춘 CI 통합 모드도 포함돼 있다.

옵션과 프로젝트 구성

주요 옵션은 다음과 같다.

옵션기능
옵션 없음 또는 --run캐시를 사용하는 실행 모드로 컴파일하고 실행
--build 또는 -o NAME바이너리 빌드. NAME은 선택 사항
--check바이너리 없이 안전성 분석만 실행
--strict-safety안전성 발견 사항이 빌드를 중단. 기본값은 경고
--no-safety메모리 안전성 분석 건너뛰기
--no-cache실행 캐시 건너뛰기
-I DIR추가 헤더 디렉터리 지정
CPPC_CACHE=DIR, CPPC_VERBOSE=1캐시 위치와 상세 출력 설정

저장소의 구성은 컴파일러 드라이버와 안전성 분석 코드가 있는 src/, 관련 헤더가 있는 include/cppc/, 런타임 소스가 있는 rt/, 테스트 묶음이 있는 tests/, 호환성 연구 자료가 있는 docs/, 빌드와 테스트를 위한 Makefile로 이뤄진다. 저장소는 이 폴더만으로 빌드·테스트·사용에 필요한 내용을 갖춘 자체 완결형이며, 참조 트리나 외부 소스는 필요하지 않다고 설명한다. 공개 대상에는 README.md, LICENSE, .gitignore도 포함된다.

저장소 안내에 실린 공개 명령은 다음과 같다.

text
cd cppC-compiler
git remote add origin https://github.com/YOURUSER/cppC.git
git push -u origin master

프로젝트는 MIT 라이선스를 사용한다. 호스트 컴파일러는 시스템의 g++이며, cppC 자체 코드는 드라이버와 분석기, 런타임 소스로 구성된다. FAQ는 #include <iostream>, std::string, 템플릿, -I 디렉터리를 사용할 수 있다고 설명한다. 기본 설정이 경고이므로 발견 사항을 살펴보는 동안 기존 빌드를 중단하지 않는다는 설명도 덧붙인다.

FAQ는 검사기가 작은 C++ 바이너리에서 한 번의 선형 토큰 스캔을 수행하므로 실제 C++ 컴파일에 비해 오버헤드가 무시할 만하다고 주장한다. 또한 ASan·UBSan 같은 새니타이저는 버그가 발생하고 실행될 때 탐지하지만, cppC는 테스트에서 실행되지 않는 경로를 포함해 자체 코드의 모든 줄에서 버그 패턴을 빌드 중 표시하며 특별한 빌드나 속도 저하가 필요 없다고 설명한다.

기여 항목

저장소는 탐지 범위를 넓히기 위한 기여 과제로 메모리 누수 탐지, 분기·반복문별 상태를 추적하는 흐름 민감 분석, 지역 변수와 매개변수의 널 상태를 다루는 널 역참조 분석을 제시한다. 그 밖에 함수 호출을 넘는 별칭 추적, 백엔드 빌드에 범위·널 검사를 삽입해 위반 시 file:line 패닉을 내는 런타임 계측, malloc/free 추적, V8·LLVM·sqlite 같은 대형 프로젝트에서 거짓 양성과 누락을 분류하는 정밀도 보고, Linux에서 gcc·clang으로 make test를 실행하는 GitHub Actions 작업도 제안한다. 이슈와 PR을 환영하며, 목록의 항목이나 별도 아이디어를 선택할 수 있다고 안내한다.

저장소의 자료와 사용 안내는 GitHub 저장소에서 확인할 수 있다.