반응형

제목: 임베디드 시스템을 위한 정적 코드 분석(Static Code Analysis For Embedded Systems)

저자: MAGNUS AGREN, 스웨덴

문서유형: 석사논문( 34페이지), 2009

 

CC++을 위한 정적 코드 분석기 5개를 선정하여 그 분석 능력(결함 발견 능력)을 조사한 자료



정적 코드 분석(Static code analysis)

  • 임베디드 시스템을 위한 많은 소프트웨어가 C 언어로 작성되지만 수동 메모리 관리나 기타 불확실성(insecurities) 때문에 오류 발생이 쉽다는 특징을 가짐
  • 이런 문제에 대한 대책 중 하나로 정적 코드 분석(소프트웨어 실행 없이 소스 코드를 분석)을 들 수 있음
  • 코드가 실행가능할(executable) 필요가 없으므로 분석이 개발 초기에 적용될 수 있으며, 조기 피드백은 코드가 중앙 저장소로 등록되기 전에 개발자가 결함을 수정할 수 있게 함


정적 코드 분석기의 결함 발견 능력 조사

  • Ascom Wireless Solutions은 무선 통신을 위한 임베디드 시스템을 개발하는 업체로 개발 업무를 개선할 방법으로 정적 코드 분석이 제안됨
  • 이 논문은 Ascom에서 개발한 실제 코드에 기존 정적 분석 도구를 적용해 보는 선행 연구(pilot study) 결과를 기술함
  • 분석 대상이 되는 코드가 CC++로 작성되었으므로 조사 대상 도구가 CC++ 언어 구조물(constructs)을 처리할 수 있어야 함


CWE 결함 분류

  • 정적 코드 분석의 결함 식별 능력에 대한 조사가 실험의 궁극적 목적이므로 소프트웨어 결함에 대한 이해를 돕는 결함 분류표를 활용함
  • CWE(Common Weakness Enumeration)은 미국의 비영리 연방 연구 단체인 MITRE에 의해 유지보수 되는 결함 분류 체계로 보안 결함(security flaws)에 중점을 둠. , CWE의 목록에 있는 많은 결함들이 보안 취약성과 관련됨(악의적인 의도를 가진 공격에서 활용될 수 있는 결함)
  • 본 논문은 CWE에 나열된 결함들 중 악의적 의도가 아닌 정상적인 프로그램 실행 중에 실패로 이어질 수 있는 결함을 아래와 같이 선정함

  • 자원 고갈(Resource exhaustion): 제한 없는 양의 자원이 할당되고 결코 자유롭게 되지 않음
  • 메모리 누수(Memory leak): 할당된 메모리가 마지막 참조가 제거되기 전에 자유롭게 되지 않음
  • 이중으로 풀어줌(Double free): 하나의 메모리 주소가 연속적으로 두 번 자유롭게 됨
  • 풀어준 후에 사용(Use after free): 이미 할당 해제(deallocated) 된 후에 메모리가 참조됨
  • 할당되지 않은 메모리를 풀어줌(Freeing unallocated memory): 동적으로 할당된 메모리를 참조하지 않는 포인터 상에 free 함수가 호출됨
  • 널 포인터 역참조(Null pointer dereference)
  • 어레이 인덱스의 경계를 벗어남(Array index out of bounds)
  • 데이터 경합(Data race): 두 오퍼레이션 간에 항상 일정할 것으로 기대되는 데이터가 변경될 수도 있음
  • 데드 코드(Dead code): 주변 문맥(context)으로 인해 실행될 수 없는 코드


평가 대상 정적 분석 도구

가용한 도구 중에서 서로 다른 이론적 기반을 가진 5개 도구를 선택: Ascom에서 이미 사용중인 단순한 상용 도구, 단순한 무료 도구, 좀 더 이론적인 연구 중심의 도구, 두 개의 고급 상용 도구

 

1. Lint

  • 1978년에 나온 도구로 C 언어로 작성된 프로그램의 의심스러운(버그나 모호함) 구조물을 타겟으로 함. , 선언되었지만 절대 사용되지 않는 변수나 함수, 도달 불가능한 코드(unreachable code), 포인터를 역참조 하면서 그 값은 무시하는 문장 등
  • Lint의 최초 버전은 Unix 시스템에서 동작했으며, 타 시스템과 언어를 위한 Lint와 유사한 프로그램이 이후 개발됨. , JLint, Splint


2. Cppcheck

  • CC++ 코드의 정적 분석을 위한 무료 소프트웨어 도구
  • 메모리 누수, 할당 해제 후에 참조 사용, 경계를 벗어난 에러(out of bounds errors) 같은 버그를 체크함. 이 때 거짓양성(false positives)이 없는 것을 목표로 함
  • 추가 체크로 버퍼 오버런, 어레이 인덱싱 경계 벗어남(array indexing out of bounds) 등을 활성화 시킬 수 있음(, 거짓양성의 가능성이 있음). 또한 항상 같은 값으로 평가가 되는 조건(conditions), 사용되지 않는 함수 형태의 데드 코드 체크도 선택적으로 가능


3. Saturn

  • 스탠포드 대학의 연구 플랫폼으로 정밀도를 희생시키지 않으면서 높은 확장성을 가진 분석 기법을 개발하는 것이 프로젝트 목표임
  • 플랫폼이 프로그램에 사용된 모든 함수의 요약 정보를 계산하고, 이후 모든 분석에서 이 요약 정보가 실제 코드를 대신하여 사용됨
  • 분석은 특수 목적 로직 프로그래밍 언어인 Calypso로 작성된 제약 시스템(systems of constraints)”으로 표현됨. 이런 분석의 한 예인 널 포인터 역참조Saturn 배포 버전에 포함되어 있음


4. Coverity Prevent

  • 상용 도구. 커맨드 라인 도구들의 집합과 로컬 http-서버로 실행되는 데이터베이스(GUI 수반)로 이루어짐
  • 별개의 애플리케이션들이 여러 다른 개별 태스크(, 구성, 빌드, 분석, 데이터베이스로 영구 반영)를 수행함. 전형적인 작업 흐름은 아래와 같다.
    1)
    프로젝트를 빌드하는데 사용되는 각 컴파일러를 위한 구성이 생성되고,
    2)
    각 컴파일러 호출을 추적하면서 정규 프로젝트 빌드가 실행되고(, 관심 대상의 모든 소스 파일이 분석을 위해 처리되고 이들의 상호 의존성도 해결됨),
    3)
    실제 분석이 수행되고 그 결과를 데이터베이스로 영구 반영하고(commit),
    4)
    이 결과가 웹 브라우저 또는 Eclipse IDE에서 플러그인을 통해 검토 될 수 있음


5. Klocwork Insight

  • Prevent에 견줄만한 상용 도구로 Prevent와 많은 유사성을 가짐
  • Insight도 대부분이 커맨드 라인 도구인 애플리케이션들의 집합과 로컬 http-서버로 실행되는 데이터베이스(GUI 수반)로 구성됨
  • 태스크가 여러 다른 애플리케이션으로 분리됨(, 구성, 빌드 분석 등). 작업 흐름도 Prevent와 대체로 비슷하고 분석 결과가 웹 브라우저나 Eclipse에서 검토 될 수 있음


정적 코드 분석 도구 선행 연구에 사용된 코드와 결함

  • 도구에 넣어줄 입력 데이터로 Ascom에서 개발된 두 개 프로그램 코드가 사용됨
    -
    하나는 Amazon이라 불리는 코드 베이스로 DECT 전화의 전체 펌웨어이며(300kLOC),
    -
    다른 하나는 전화 알람 핸들링 기능을 구현한 독립 모듈로 주변의 둘러싼 시스템을 위한 wrapper 스터브를 가짐(9kLOC)
  • 이 두 가지 프로그램에서 실제 발견된 3개 결함(데이터 경합, 제거 후 참조, 메모리 누수)의 예를 정적 분석 도구에 입력하여 도구가 이를 식별해 낼 수 있는지를 조사
  • 아래는 이 3개 결함 예의 소스 코드(실제 프로그램의 단순화된 버전으로 결함의 원래 형태는 최대한 유지하면서 불필요한 추가 정보는 삭제함)


1. 데이터 경합(Data race)

  • 함수 hazard가 내적으로 수행되는 작업을 위해 라인 14static으로 선언된 변수 accum을 사용하고, 함수 f는 한 범위의 정수값(integers)들의 합을 계산하기 위해 hazard를 사용함
  • 이 두 가지 사용(usage) 모두 lock에 의해 보호되지 않음. f가 라인 45 46에서 두 개 쓰레드로 갈라질 때, 쓰레드가 인터리빙 되면 라인 31~34의 루프가 정수값을(각각 1~100002000~3000) 정확하게 합계하지 않음



2. 제거 후 참조(Reference after removal)

  • 연결 리스트(linked list)에서 노드를 삭제 후에 이 노드로의 참조가 일어난 예
  • 리스트 노드들의 타입은 라인 10~13에 정의된 list_t이며, 모든 가용한 노드는 라인 15에서 정적으로 할당됨
  • 함수 init_list 4개의 임의 노드(arbitrary nodes)로 리스트를 생성하고, 함수 remove_list_entry는 이 리스트로부터 하나의 노드를 제거한다.
  • 라인 64에서 리스트로부터 하나의 노드가 제거된 직후 라인 66에서 이 리스트 노드를 참조하는 것이 의미가 없음(, 항상 라인 69else-브랜치가 선택됨)
  • list_t가 어떤 데이터로의 포인터를 포함하는 것이 아니라 단지 리스트의 포인터 구조를 포함함에 주의해야 함


3. 메모리 누수(Memory leak)

  • 할당된 메모리로의 모든 참조를 잃지만 절대 자유롭게는 되지 않는 자원 누수 경로가 존재함(라인 17의 할당à라인 24의 브랜치à라인 26의 호출à라인 5에서 브랜치가 선택되지 않음). 다시 말하자면 GetRelease가 할당과 할당해제 함수로써 쌍으로(pairwise) 작동하도록 의도되었지만, 모든 경로가 의도에 맞게 동작하지는 않음
  • 라인 7에서 할당 해제가 할당된 메모리의 값에 의존하도록 만들어진 것은 분석 도구 평가를 위한 추가 테스트임. , 이 동작은 여러 다른 도구가 어느 정도 동적 메모리를 핸들링하는지 보기 위해 코드 단순화를 한 후에 예로 추가된 것임


평가 결과

1. Lint

  • Ascom에서 사용되는 PC-Lint 버전이 3개 결함 예 중 2개를 식별함
  • 데이터 경합 예의 경우 라인 4546에서 경고(warnings)가 나타나 pthread_create로부터의 리턴 값이 무시되었다고 알림. PC-Lint는 코드의 주석(annotations)을 통해 함수의 시맨틱에 대한 추가 정보를 받을 수 있음. 함수 f가 쓰레드임을 선언하는 주석이 제공될 때는 라인 3033에서 hazard 호출에 대한 경고가 나타나 accum의 사용이 보호되지 않았다고 알림
  • 두 번째 결함 예인 제거 후 참조에 대해서는 도구가 아무것도 보고하지 않음
  • 세 번째 결함 예인 메모리 누수에 대해서는 아래와 같은 3개의 경고가 나타남
    -
    라인 32Get 함수 뒤에서 포인터 pVal이 어떤 경로에서도 할당 해제 되지 않는다
    -
    라인 13Release 함수 뒤에서 “argument 포인터 a_pDynamic이 절대 할당 해제되지 않는다
    -
    라인 10Release 함수에서 a_pDynamic으로 할당된 마지막 값이 사용되지 않는다
  • Amazon 코드 베이스는 1,757개의 숨은 경고(suppressed warnings)”를 포함. 숨은 경고는 경고가 수작업으로 검토되었고 거짓양성(false positive)으로 분류되었음을 의미. 알람 핸들링 모듈은 51개의 숨은 경고를 포함


2. Cppcheck

  • 3개 결함 예에 대해서는 어떤 체크가 활성화 되는지에 상관 없이 어떤 보고도 하지 않음
  • 추가 체크를 활성화하지 않은 채로 Amazon 코드 베이스 상에서 실행될 때는 2개 결함을 발견함. 이 두 결함은 입력과 출력 모두에 같은 포인터가 사용된 sprintf 라이브러리 함수와 관련(하나의 문자열의 콘텐츠를 다른 문자열에 쓰는 함수). C99에 따르면 입력 버퍼와 출력 버퍼가 겹치면(overlap) 동작이 미확정(undefined)이다. 

sprintf(buf, "%s", buf); 

  • 모든 체크가 활성화된 상태에서 CppcheckAmazon에 실행한 결과가 아래 표에 요약됨

  • 모든 체크가 활성화된 상태로 Cppcheck가 알람 핸들링 모듈에 실행되었을 때 생성자(constructor)가 없는 클래스 1, non-virtual destructor를 가진 클래스를 상속 받는 경우 1, 알려진 거짓양성 메모리 누수 4건을 발견함


3. Saturn

  • SaturnC만 처리할 수 있고 C++은 다루지 못함(선행 연구를 위해 도구 선정 시 이점을 간과). 메모리 누수 예에서 널 포인터 분석이 요구되지만 이것이 C++로 작성되어 문제가 됨
  • Amazon의 상당 부분은 C로 작성됨. C 소스 코드가 Saturn에 의해 분석될 수 있으려면 그 전에 C preprocessor를 통해 실행될 필요가 있음(매크로 확장, 헤더 파일 포함 해결 등). Amazon이 사용하는 운영체제를 위한 라이브러리 헤더 파일이 타겟 마이크로프로세서에서 컴파일러 최적화를 용이하게 하기 위한 일부 C 확장(extensions)을 포함하는데 Saturn이 이것을 처리하지 못함
  • 이런 여러 문제로 인해 Saturn은 추가적인 평가에서 제외됨


4. Coverity Prevent

  • 상용 도구이므로 시험 라이선스를 얻어 분석을 수행하고, Coverity와 웹컨퍼런스를 열어 데이터베이스로 분석 결과를 영구 반영(commit)하는 방법을 원격 지도 받음(여기서 결과가 수작업으로 검토되고, ‘결함’, ‘거짓양성’, 또는 의도대로 동작으로 분류됨)
  • 분석 대상 결함 예에서는 메모리 누수 관련 한 건의 자원 누수(resource leak)가 보고되었고, 다른 두 개 결함의 분석에서는 아무런 보고도 나오지 않음
  • 시험 라이선스로는 분석 결과를 데이터베이스에 넣고 결함을 수작업 검토 하는데 제한이 있어서 알람 핸들링 모듈은 생략하고 Amazon 코드 베이스만 분석함
  • Prevent Amazon 코드 베이스를 빌드하는 것이 쉽지 않았음(Prevent의 제공 문서에는 Amazon이 사용하는 컴파일러가 지원된다고 써 있지만, 이 컴파일러 벤더가 제공하는 표준 CC++ 라이브러리가 Prevent가 파싱하지 못하는 템플리트 구현을 포함하고 있었고, 또한 벤더의 custom syntax 관련 다른 파싱 문제도 일어남)
  • 디폴트 옵션으로 Amazon 코드 베이스를 분석한 일차 결과가 아래 표와 같음

  • 위 보고된 결과를 수작업 검토를 하여 분류하면 아래와 같음


5. Klocwork Insight

  • 2주간 무제한 사용을 허용하는 시험 라이선스를 얻어 분석을 수행함
  • 세 개의 결함 예 분석에서 메모리 누수 문제가 정확하게 식별되었지만 다른 두 개의 결함에 대해서는 아무런 보고를 하지 않음
  • 알람 핸들링 모듈의 분석에서는 4개 결함이 보고되었고, 수작업 검토 후에는 이 중 세 개가 결함으로 나머지 하나는 의도대로 동작하는 것으로 분류됨
  • InsightAmazon을 빌드하는데 별 어려움이 없었고 시험 라이센스가 빌드 수나 데이터베이스 반영에 대한 제한을 하지 않음. 아래는 Amazon 분석의 일차적인 결과임


  • 위 보고된 결과를 수작업 검토를 하여 분류하면 아래와 같음


결론

  • 정적 코드 분석이 개발 초기에 실행불가능 코드(non-runnable code)에 적용 가능하다고 주장되지만, 분석 대상 코드에 Coverity PreventSaturn이 동작하도록 만드는데 있어서의 어려움이나 보고된 많은 수의 거짓양성(false positives)은 이런 주장을 반박함
  • 선행 연구의 경험에 비추어 보면 정적 코드 분석 도구가 실무에 사용되기 위해서는 아래와 같은 요구사항들이 요구됨
    -
    결함 보고의 수작업 사후 프로세싱(, 실제 결함인지 아닌지를 결정)이 제한되어야 함
    -
    거짓양성의 생성이 거의 없어야 하고, 의심되는 결함이 실제 어디에 위치하는지가 보고에서 명확하게 드러나야 함
    -
    도구가 빌드 프로세스와 잘 통합되어야 하며, 분석 대상 코드에서 사용되는 컴파일러와 라이브러리를 지원해야 함
    -
    도구가 C preprocessor를 정확하게 다룰 수 있고, 매크로도 제대로 확장할 수 있으며, 조건부 컴파일(conditional compilation)도 다룰 수 있어야 함
  • 이 연구의 결과는 정적 코드 분석이 실제 제품 코드(production code)에서 실제 발생하는 결함을 찾는데 실현가능한 방법임을 보여줌


반응형

+ Recent posts