[테크월드=정환용 기자] 현재의 소프트웨어 산업의 가장 큰 화두 하나는 소프트웨어의 품질이다. 빠르게 발전하는 소프트웨어 산업으로의 다양성 확대, 기능 확장, 복잡도 증가 등의 이유로, 소프트웨어 검증, 테스팅 등의 품질 관련한 이슈가 많아지고 있다. 소프트웨어 개발 중 품질 검사, 또는 개발자의 다양한 코딩 실수 검사 등을 할 수 있는 IAR Systems의 코드 분석 도구인 ‘C-RUN’, ‘C-STAT’을 소개하고, 사용 예제를 이용해 설명한다.

Static vs Runtime
정적(Static) 방법의 분석과 실행/동작(Runtime) 방법의 분석 모두 코드가 가지고 있는 오류 또는 취약점을 찾아내주는 분석 방법의 종류다. 정적 분석은 작성돼 있는 소스코드를 대상으로, 코드 안에 내포돼 있는 잠재적인 취약점을 분석하는 방법이다. 동작 분석 방법은 실제 코드를 실행시켜 실행 중 발생하는 오류를 분석하는 방법으로, 두 방법에는 차이가 있다.

IAR Systems의 코드 분석 도구 중 C-STAT은 작성된 코드를 분석하는 정적 분석도구이고, C-RUN은 실제 실행 코드를 타깃에 다운로드 후 실행 중 발생하는 오류를 분석하는 동적 분석도구에 해당한다.

▲[그림 1] Static vs Runtime

 

▲[그림 2] C-CTAT Static Analysis

 


C-STAT Static Analysis
C-STAT은 ‘IAR Embedded Workbench’에 통합돼 있는 추가 제품이다. IAR Embedded Workbench 이외에 별도의 설치를 할 필요가 없으나, C-STAT의 기능을 사용하기 위해 C-STAT 사용을 위한 라이선스 추가가 필요하다. C-STAT은 정적 분석으로 작성된 코드만으로 코드 내의 잠재적 문제들을 쉽게 파악해, 개발 초기부터 코드 품질, 더 나아가 제품의 품질을 높여줄 수 있다.

C-STAT의 주요 기능은 다음과 같다.
▲ C/C++ 언어의 코드 분석
▲ MISRA C:2004, MISRA C++:2008, MISRA C:2012에 정의된 규칙 준수 확인
▲ CWE와 CERT C/C++에 따른 많은 문제에 기반을 둔 다양한 검사항목
▲ 직관적이고 사용하기 쉬운 설정
▲ 개별 규칙 단계뿐만 아니라 규칙 설정 단계의 유연한 규칙 선택
▲ IAR Embedded Workbench IDE와 완벽하게 통합
▲ 다양하고 상세한 오류 정보 표시
▲ 빠른 분석 실행 속도

C-STAT의 정적 분석으로 사용할 수 있는 기능 중 하나로 MISRA-C 코딩 룰을 검사하는 기능이 있다. MISRA-C 코딩 룰은 MISRA(Motor Industry Software Reliability Association)에서 개발된 자동차 산업 제품의 소프트웨어 개발에 사용되는 C 프로그래밍에 대한 코딩 룰이다. 소프트웨어의 코드 안전성, 호환성, 신뢰성 향상을 목표로 하고 있다. 

C-STAT에서는 MISRA-C의 MISRA-C:1998, MISRA-C:2004, MISRA-C++:2008, MISRA-C:2012 버전의 코딩룰 검사를 지원하고 있다. MISRA-C 코딩 룰을 준수해 진행되는 프로젝트의 개발자가, 코드 작성 후 바로 코딩 룰 검사를 실행해 볼 수 있는 장점이 있다. 또한, CERT C/C++의 일부 코딩 룰 확인이 가능하며, CWE 리스트 기반의 개발자가 범할 수 있는 실수를 검사할 수 있는 다양한 표준 검사항목을 제공한다.

 

▲[그림 3] C-RUN Runtime Analysis

C-RUN Runtime Analysis
C-RUN은 C-STAT과 마찬가지로 IAR Embedded Workbench에 통합돼 있는 추가 제품이다. IAR Embedded Workbench 이외에 별도의 설치를 할 필요가 없으나, 기능의 활성화를 위해 라이선스를 추가해야 한다. 다만 C-RUN의 경우 추가 라이선스를 구매하지 않더라도 코드사이즈 제한(테스트 코드 12K 제한)으로 평가할 수 있다. C-RUN은 실행 분석으로 작성된 소스 코드를 실제 동작의 타깃에 다운로드해, 동작 시킨 후 발생하는 문제점에 대해 분석한다.

C-RUN의 주요기능은 다음과 같다.
▲ C/C++ 언어의 코드 분석
▲ 직관적이고 사용하기 쉬운 설정
▲ 최적화된 테스트 코드 삽입으로 코드 사이즈와 성능 저하를 최적화
▲ 다양하고 상세한 런타임 오류 정보 표시
▲ 콜 스택 정보를 포함한 오류 정보 표시 
▲ 오류 사항과 코드와의 상관관계 확인, 에디터 창에 오류 표시
▲ 유연한 필터 관리
▲ 배열 또는 범위를 가지고 있는 객체들의 경계범위를 벗어난 접근 검사
▲ 버퍼 오버플로우 검사
▲ 서로 다른 타입의 변수를 캐스팅 할 경우 발생하는 값의 변화 검사
▲ 산술연산에서의 오버플로우 검사
▲ 쉬프트 연산에서의 오버플로우 검사
▲ 힙 메모리의 잘못된 사용과 메모리 누수 검사

일반적인 동적/실행 분석 도구들은 코드 실행 중 검사를 하기 때문에, 소스 코드 중 테스트돼야 할 위치에 테스트 코드를 사용자가 직접 입력해야 한다. 따라서 전체 소스 코드의 크기가 커지고, 테스트 코드와 같이 실행되며 코드 실행 성능이 떨어지게 된다. 하지만 C-RUN의 경우 테스트 코드의 입력을 컴파일러가 코드를 분석해 최적화된 코드를 자동 삽입하기 때문에, 코드 사이즈 증가와 실행 성능 저하를 최소화한다.

 

코드 분석 도구를 활용해 코드 품질 향상
다음의 예제 코드를 이용해 코드 품질 향상을 위한 C-RUN과 C-STAT을 활용 방법을 설명한다. 우선 다음의 예제는 strcpy(),strcat() 의 라이브러리 함수를 이용해 “Hello World!”라는 문구를 만들고, 이를 출력하는 두 방법에 대하여 작성돼 있다.

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

const char str1[] = “Hello “;
const char str2[] = “world! “;

int main()
{
char buf1[12];
char *buf2;
int length;
strcpy(buf1, “Hello “);
strcat(buf1, “world!”);
printf(“%sn”, buf1);
length = strlen(str1) + strlen(str2);
buf2 = (char *)malloc(sizeof(char) * length);
strcpy(buf2, str1);
strcat(buf2, str2);
printf(“%sn”, buf2);
return 0;
}

앞에서 작성된 예제의 빌드과정에 오류, 경고가 없이 정상 빌드가 진행된다. 이 후 C-SPY 디버거에서 코드를 실행시키면 Teminal I/O 창(View > Terminal I/O)에서 정상적으로 “Hello World!” 문구가 2개 출력됨을 확인할 수 있다.

동일한 예제를 C-STAT으로 정적 분석을 진행하면 다음과 같은 결과가 출력된다. Project Option > C-STAT Static Analysis > Analyze Project 에서 검사항목을 설정한다. C-STAT의 정적 분석으로 C-STAT Messages 창에 결과를 표시한다. 분석된 결과의 내용을 보면, 2개의 코드에 내포돼 있는 문제점을 찾아낼 수 있다.

 

▲[그림 4] C-RUN Static Analysis
▲[그림 5] C-STAT 설정
▲[그림 6] C-STAT 실행
▲[그림 7] C-STAT 분석 결과

내용을 보면 첫 번째 메시지는 스트링의 경우 마지막에 스트링의 마지막을 표시하는 Null 값을 표시해야 하지만, 스트링 값이 저장되는 배열 변수의 길이가 스트링의 마지막 Null 값을 포함할 수 없는 길이이기에 나오는 메시지다. 두 번째 메시지는 malloc() 함수를 이용해 힙 메모리 시작 주소를 return 받을 경우, malloc() 함수 처리 중 정상적으로 힙 메모리 할당이 되지 않으면 Null 값을 return 하지만 이에 대한 예외 처리가 없어 나오는 메시지다. 따라서 코드 품질의 향상을 위하여 다음과 같이 코드를 수정한다.

이번엔 앞의 수정된 C-RUN을 사용해 실행 분석을 진행한다. Project Options > Runtime Checking 에서 C-RUN 검사항목 설정 후 코드를 빌드한다. 이후 C-SPY 디버거에서 코드를 실행시켜 코드 실행 분석을 진행한다.

 

▲[그림 8] C-RUN 설정
▲[그림 9] C-RUN에서의 Runtime 오류 검출

C-RUN의 동적 분석으로 실제 실행 중 발생하는 문제점을 분석해 C-RUN Messages 창에 결과를 출력한다. 예제 코드를 실행한 결과, 하나의 실행 분석 메시지가 출력됐다. 오류가 발생한 위치와 오류 내용을 자세히 보면 strcat() 함수를 이용해 힙 메모리에 위치한 buf2에 문자열을 추가하는 과정에서, 마지막의 Null 까지 입력하게 돼 buf2의 범위를 벗어나게 됐다. 원인은 힙 메모리를 할당받은 크기가 스트링의 마지막 구분을 위한 Null을 포함하지 못하고 있어 문제가 나타난 것이다. 따라서 코드 품질의 향상을 위해 다음과 같이 코드를 수정한다.

 

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

const char str1[] = "Hello ";
const char str2[] = "world! ";

int main()
{
char buf1[13]; // 배열길이를 스트링의 마지막 Null 을 포함하도록 수정
char *buf2;
int length;
strcpy(buf1, "Hello ");
strcat(buf1, "world!");
printf("%sn", buf1);
length = strlen(str1) + strlen(str2);
buf2 = (char *)malloc(sizeof(char) * length);

if(buf2 == NULL) return 1; // buf2에 Null 값이 return 될 경우의 예외 처리 추가
strcpy(buf2, str1);
strcat(buf2, str2);
printf("%sn", buf2);
return 0;
}

 

맺음말
이 예제에서도 동작에 문제가 없어 보였으나, 코드 분석을 통해 내포돼 있는 잠재적 문제를 확인할 수 있었다. 이런 문제들로 인해 큰 문제가 개발 후반기 또는 출시 이후 발생된다면, 문제 해결 비용이 큰 부담이 될 수 있다.

소프트웨어 품질에 대한 중요성은 누구나 인식하고 있다. 제품에 내재된 문제를 개발 초기에 발견하고 문제를 해결한다면, 문제 해결에 발생하는 비용을 최소화할 수 있다. 초기 문제 발견이 되지 않고 추후 개발 후반기 또는 출시 이후 발견이 된다면, 초기에 문제를 해결할 때보다 문제 해결 비용이 기하급수적으로 늘어나게 된다. 따라서 C-RUN, C-STAT과 같은 코드 분석 도구를 개발 초기에 잘 활용해 문제를 개발 초기에 발견하고 해결한다면, 소프트웨어의 품질 향상과 문제해결 비용 최소화에 도움이 많이 될 것이다.

 

글: 이현도 과장 | IAR 시스템즈 코리아 기술지원팀
자료제공: IAR시스템즈코리아(www.iar.com)

회원가입 후 이용바랍니다.
개의 댓글
0 / 400
댓글 정렬
BEST댓글
BEST 댓글 답글과 추천수를 합산하여 자동으로 노출됩니다.
댓글삭제
삭제한 댓글은 다시 복구할 수 없습니다.
그래도 삭제하시겠습니까?
댓글수정
댓글 수정은 작성 후 1분내에만 가능합니다.
/ 400
내 댓글 모음
저작권자 © 테크월드뉴스 무단전재 및 재배포 금지