https://www.fuzzingbook.org/html/Coverage.html
- Coverage란, 소프트웨어 테스팅을 하는데 사용되는 지표 중 하나로, Code가 얼마나 실행되었느냐를 측정한다.
- Coverage를 측정하기 위해 instrumentation을 한다.
- instrumentation은 작성된 코드가 실행되는 지를 확인하는 코드를 작성된 코드 사이사이에 삽입하는 것을 뜻한다.
- Function coverage : 함수가 최소한 한 번 실행되었는 가를 측정한다. function call을 했는지만을 확인하기 때문에 해당 function안의 모든 코드가 실행되었는 지는 확인할 수 없다.
- Statement coverage : 각 statement가 실행 되었는지를 측정한다.
- 장점 : object code에 바로 적용 될 수 있다.
- 단점 : 특정 조건분기문에서 에러를 찾아내지 못할 가능성이 있다.
1
2
3
4int *p = NULL;
if(condition)
p = &a;
*p = 123;
- condition이 false라면 null pointer error가 발생한다. 이때 coverage가 달성되었다고 하겠지만, 모든 경우를 test했다고 보장 할 수 없다.
- Brach coverage : 각 조건 분기문이 참/거짓으로 모두 실행되었는 지를 측정한다.
- 장점 : Statement coverage가 가지고 있는 단점을 보완한다.
- 단점 : 조건문 전체를 확인해보지 않기 때문에 에러를 찾아내지 못할 가능성이 있다.
1
if(condition1 && (condition2 || function()))
- condition2가 참이되는 순간 function()을 실행하지 않기 때문에 function()함수가 test되지 않는 경우가 발생한다.
- Condition coverage : 조건 분기문에 있는 모든 expression들의 참/거짓을 측정한다.
- 장점 : Branch coverage의 단점을 보완한다.
1
if(condition1 || condition2 && condition3)
- 장점 : Branch coverage의 단점을 보완한다.
- condition1, condition2, condition3가 각각 모두 참/거짓인 경우를 확인한다.
- Path coverage : 각 function에서 가능한 모든 Path를 실행해봤는지를 확인한다.
- path란 함수의 시작부터 끝까지 존재하는 연속된 branch들을 뜻한다.
- 각 path는 unique하다.
- 장점 : 엄밀한 테스팅이 가능하다.
- 단점 : Path의 수가 매우 많다.
Synopsis
- coverage 정보를 uncoverd locations로 fuzzing을 유도할 수 있다. (guided fuzzing)
A CGI Decoder
- CGI encoding은 URL에서 사용된다.
- 공백은 ‘+’로, invaild한 문자는 ‘%xx’ 로 바뀐다. (xx는 두자리 hex)
- “Hello, world!” -> “Hello%2c+world%21”
1 | def cgi_decode(s: str) -> str: |
- cgi_decode함수를 체계적으로 테스트 하고싶다면 두 가지 방법이 있다.
- BlackBox testing과 WhiteBox testing
Black-Box Testing
- 블랙박스 테스트는 specification으로부터 테스트를 도출하는 것이다.
- 내부 구조나 동작 원리를 모르는 상태에서 소프트웨어의 동작을 검사하는 것.
- testing for correct replacement of ‘+’;
- testing for correct replacement of “%xx”;
- testing for non-replacement of other characters; and
- testing for recognition of illegal inputs.
White-Box Testing
- 화이트박스 테스트는 내부 구조를 가지고 테스트를 도출하는 것이다.
- 코드의 구조적 특징을 다루는 개념과 밀접한 관련이 있다.
- The block following if c == ‘+’
- The two blocks following if c == ‘%’ (one for valid input, one for invalid)
- The final else case for all other characters.
Tracing Executions
- 파이썬에서는 함수 sys.settrace()를 사용하면 추적이 가능하다.
- 모든 라인에 대해 호출되는 추적함수 f를 정의하고 sys.settrace(f)를 호출하면 된다.
- 추적함수에는 3가지 매개변수가 있다. (frame, event, arg)
- frame 매개변수는 현재 frame을 가져와 현재 위치와 변수에 접근할 수 있다.
- frame.f_code는 현재 프레임과 함께 생성되는 코드이다.
- frame.f_code.co_name은 함수 이름이다.
- frame.f_lineno는 현재 라인 번호이다.
- frame.f_locals는 지역 변수와 인자값 이다.
- event 매개변수는 문자열이 들어간다.
- “line”이면, 호출된 새로운 line,
- “call”이면, 호출된 함수
- agr 매개변수는 어떠한 event에 대한 추가적인 인자이다.
- “return”이벤트일대 arg는 리턴 값을 포함한다.
- frame 매개변수는 현재 frame을 가져와 현재 위치와 변수에 접근할 수 있다.
1 | def traceit(frame: FrameType, event: str, arg: Any) -> Optional[Callable]: |
- 추적함수 traceit을 선언해준다. 새로운 line이 호출되면, 호출된 lineno을 coverage 전역변수에 추가해주는 함수이다.
1 | def cgi_decode_traced(s: str) -> None: |
- sys.settrace()함수로 추적함수를 실행 시킨 후, 추적할 함수 cgi_decode()를 호출한다.
- 그 후 sys.settrace(none)함수로 추적을 종료한다.
1 | for lineno in range(1, len(cgi_decode_lines)): |
- 추가적으로 위의 코드를 실행시키면, 커버되지 않은 line과 커버된 line에 표시를 할 수 있다.
1 | with Coverage() as cov: |
- “a+b”를 주었을 때 if c==”+” block을 커버하고, elif문을 커버하지 않는 것을 확인할 수 있다.
A Coverage Class
1 | with OBJECT [as VARIABLE]: |
- 파이썬으로 커버리지를 측정할때 일반적인 형식은 OBJECT가 정의되고 BODY가 실행되는데,
- 이때 OBJECT.enter()과, OBJECT.exit()가 자동으로 호출된다.
- Coverage.enter()은 자동으로 추적을 시작하고.
- Coverage.exit()은 추적을 종료한다.
1 | with Coverage() as cov: |
- function_to_be_traced()가 진행될 동안 추적을 시작하고, with block 다음에 다시 꺼진다.
1 | Location = Tuple[str, int] |
Coverage of Basic Fuzzing
- cgi_decode() 함수를 랜덤 퍼징으로 최고의 커버리지에 도달하는 것을 목표로 한다.
1 | with Coverage() as cov_fuzz: |
- maximum 커버리지라고 생각 될 수있지만, max 커버리지와 비교했을 때 여전히 몇 개의 라인을 놓친것을 확인 할 수 있다.
Getting Coverage from External Programs
- 거의 모든 프로그래밍 언어는 커버리지를 측정할 수 있는 기능이 있다.
1 |
|
- 파이썬과 같은 코드로 c를 짠다.
1 | cc --coverage -o cgi_decode cgi_decode.c |
- 컴파일 단계에서 –coverage 옵션을 준다. –coverage옵션은 런타임에 정보가 수집되도록 코드를 instrument한다.
1
./cgi_decode 'Send+mail+to+me%40fuzzingbook.org'
- 프로그램을 실행할 때 커버리지 정보는 파일로 자동 생성된다.
- 커버러지 정보는 gcov 프로그램에 의해 수집된다.
- 주어진 모든 소스 파일에 대해 커버리지 정보가 포함된 새로운 .gcov 파일을 생성한다.
1
gcov cgi_decode.c
- .gcov 파일에서 각 행은 호출된 횟수와 줄 번호가 앞에 붙는다.