[논문리뷰] Evaluating Large Language Models for Real-World Vulnerability Repair in C/C++ Code
0. Abstract
- 실제 코드 취약점 수정에 LLM을 활용하는 평가는 여전히 충분히 이루어지지 않음
- LLM이 실제 C/C++ 코드의 메모리 손상 취약점을 수정하는 능력을 평가
- 223개 C/C++ 코드 스니펫 선정
- 메모리 누수
- 복잡한 버퍼 오류 등
- LLM은 지역적 코드 세그먼트로 제한되는 단순한 메모리 오류(예: 누수)를 수정하는 데 능숙함을 보임
- Cross-cutting concerns 와 deeper 프로그램적인 시멘틱에서는 능숙하지는 않음
- 지식을 통합하여 LLM의 성능을 향상시키는 기술을 탐구
- LLM의 자동 프로그램 수정의 강점과 한계를 보여주며, 복잡한 코드 수정 작업을 처리하기 위해 추론 능력 향상의 필요성을 강조함
1. Introduction
- 코드 취약점을 수동으로 수정하는 것은 까다롭고 오류가 발생하기 쉬운 과정
- -> 숙련된 개발자가 코드를 면밀히 분석하고, 문제의 근본 원인을 파악하며, 정확한 패치를 적용해야 함
- -> 시간이 많이 소요
- -> 간소화하기 위해 최근에는 대규모 언어 모델(LLM)을 사용
- 실제 코드는 일반적으로 복잡한 구조, 상호 의존성, 그리고 광범위한 맥락적 뉘앙스를 가지고 있어 어렵다.
- 예를 들어, 메모리 손상과 관련된 취약점은 함수, 변수, 또는 구조체 간의 복잡한 상호작용을 포함하며, 코드 로직에 대한 깊은 이해를 요구
- 기존의 데이터 셋은 이를 반영하지 못하고 있어, LLM을 평가하는데 있어 정확하지 않았음.
본 논문은 메모리 손상 취약점 수정에 대한 LLM의 광범위한 평가를 수행(C/C++)
- 결과 :
- LLM이 메모리 누수와 같은 단순한 메모리 오류를 수정하는 데 능숙함, localized된 코드 세그먼트에서 그러함
- 그러나 cross-cutting concern 와 복잡한 프로그램 의미론에 대한 심층 분석이 필요한 문제에서는 성능이 떨어짐
- 통합 :
- 변수의 맥락, 외부 함수, 데이터 구조나 변수, 취약한 코드의 로직을 통합하여 복잡한 취약점에 대한 LLM 성능을 향상시키는 전략을 논의
- 결론적으로 LLM의 추론 능력이 더 좋아져야 한다.
2. Related Work
패치를 생성하기 위해 탐색 기반, 심볼릭 실행 기반, 딥 러닝 기반 등 다양한 접근 방식 사용
탐색 기반 방법은 (진화 알고리즘, 휴리스틱 탐색 또는 최적화 기술)을 활용하여 패치 공간을 탐색하고 효과적인 해결책을 도출
- 심볼릭 실행 기반 방법은 패치 생성 과정을 안내하는 제약 조건을 도출하기 위해 프로그램 경로를 탐색
- 그러나 복잡한 버그(패치 공간이 복잡한)의 경우 그 효과가 뛰어나지 않음.
딥 러닝 방법은 결함이 있는 프로그램과 수정된 프로그램의 광범위한 데이터셋을 활용 -> 신경망 훈련
- 새로운 버그에 대한 패치를 직접 예측
코드 수정을 위한 LLM활용 연구들
- LLM을 사용하여 초기 취약점이 있는 코드 스니펫을 생성 -> 동일한 모델을 사용하여 생성된 코드를 수정
- 그러나 이 접근 방식은 LLM이 자신이 생성한 취약점을 정확히 이해한다고 가정하나, 가정이 안맞을 수 있음.
LLM을 사용하여 LeetCode와 같은 플랫폼의 코드 문제를 분석하고 수정
- 또한 모델이 CWE나 LeetCode 문제 설명을 정확히 분석하는 능력에 크게 의존
LLM 기반 프로그램 수정의 현재 최신 기술은 주로 Java나 Python과 같은 특정 언어에 집중
- 종종 소규모 테스트 프로그램으로 제한되어 있어 일정한 제약
- 현존하는 연구에서는 LLM이 매우 적은 수의 Java 취약점만을 수정한다고 논의함(very few Java vulnuerabilities)
- Java나 Python 프로그램에서 -> C++로 매칭 시키기는 어려움
- 문법과 핵심 기능에서 차이가 있음.
- Example1) Java는 C++의 명시적 포인터와 같은 것이 없다.
- Example 2)
- Java는 가비지 컬렉션을 통한 자동 메모리 관리, 여기서 'str'은 더 이상 참조되지 않거나 범위를 벗어날 때 자동으로 할당 해제.
- 반면 C++는 'new'를 사용한 할당과 'delete'를 사용한 할당 해제와 같은 작업을 통해 수동으로 관리가 필요함.
- C++에서 메모리를 명시적으로 할당 해제하지 않으면 메모리 누수로 이어질 수 있음.
실제 소프트웨어 시스템은 복잡하고 상호 연결
- LLM이 초기 코드 스니펫을 생성하거나 제공된 쿼리를 기반으로 피드백을 제공하는 데 뛰어남
- 허나, 메모리 손상과 같은 복잡한 취약점을 다루는 데 있어서는 한계가 있음.
- 메모리 손상 -> 코드 컨텍스트 전체에 걸쳐 있으며, 복잡한 데이터 구조와 메모리 관리 기술 이해 필요.
해당 연구는 메모리 손상 취약점을 다루는 데 초점을 맞춘다.
- 버퍼 오버플로우나 이중 해제 오류와 같은 다양한 시나리오를 포함
3. Backgroud
메모리 손상 취약점은 일반적으로 (메모리 영역 내의 조작이나 / 손상 / 무단 접근 / 데이터 유출 / 시스템 충돌 / 또는 심지어 원격 코드 실행)
이 논문에서는 다음과 같은 메모리 손상 취약점과 그에 해당하는 Common Weakness Enumeration (CWE)논의
- 버퍼 오류 (CWE-119): 버퍼의 경계를 벗어난 곳에서 데이터를 읽거나 쓸 때 발생. (메모리 손상, 코드 실행)
- 입력 크기 확인 없는 버퍼 복사 (CWE-120): 입력의 크기를 확인하지 않고 데이터를 버퍼에 복사하는 것과 관련 (버퍼 오버플로우)
- 정수 오버플로우 (CWE-190): 정수 값이 최대 표현 가능한 값을 초과할 때 발생, (데이터 손상과, 예기치 않은 프로그램 동작을 유발)
- 이중 해제 (CWE-415): 프로그램이 같은 메모리 위치를 두 번 이상 해제하려고 할 때 발생, (메모리 손상, 충돌)
- 해제 후 사용 (CWE-416): 해제 후 사용. 프로그램이 이미 할당 해제된 메모리에 접근하려고 할 때 발생(메모리 손상, 충돌)
- 메모리 누수 (CWE-401): 프로그램이 메모리를 할당하고 사용 후 할당 해제하는 것을 잊어버릴 때 발생 (비효율적인 메모리 사용, 고갈)
- NULL 포인터 역참조 (CWE-476): 충돌이나 예측할 수 없는 동작을 초래 (메모리 손상)
- 메모리 누수는 덜 복잡함
- 버퍼 오류는 버퍼 오버플로우나 언더플로우와 같은 다양한 문제를 포함하며, 종종 메모리 할당, 데이터 구조, 코드 로직에 대한 복잡한 이해가 필요
LLM이 이러한 다양한 취약점 사례를 얼마나 효과적으로 다룰 수 있는지 탐구하는 것을 목표로 한다.
4. CODE PATCH GENERATION
- "LLM이 취약점의 정의와 버그 위치가 제공되었을 때 취약한 코드에 대한 패치를 효과적으로 생성할 수 있는가?"
-> 자동화된 패치 생성에 대한 LLM의 가능성을 탐구 - "어떤 조건과 시나리오에서 LLM이 취약점에 대한 능숙한 코드 패치를 생성하는 데 어려움을 겪는가?"
-> LLM이 코드 취약점에 대한 효과적인 패치를 생성하는 데 실패할 수 있는 시나리오를 밝히고자 함
LLM의 성능 평가는 GitHub에서 코드 스니펫을 검색하는 것으로 시작
이 스니펫들은 국가 취약점 데이터베이스(NVD)에 보고된 메모리 손상 취약점 사례에서 유래
이 과정에서 목표 : 취약점을 포함하는 함수와 패치가 필요한 코드 내의 정확한 위치 식별
- 특정 메모리 손상 취약점과 복잡하게 연관된 223개의 C++ 코드 스니펫을 포함하는 새로운 데이터셋을 구축
- 이 취약점들은 다음과 같이 분류
- CWE-119(30%), CWE-190(20%), CWE-416(17%), CWE-476(18%)으로 분류
- 다음으로, ChatGPT4와 Claude LLM의 이러한 취약점 해결 효과를 평가
- 각 코드 스니펫에 대해, 우리는 취약점 정의를 제공하고 코드 내에서 수정이 필요한 섹션을 식별
- 마지막으로, 패치들은 수동 검증을 통해 그 정확성을 면밀히 검토하고 잘못된 패치 내의 문제를 식별
5. Results of Automated Patching
- LLM에 프롬프트를 제공 : 구조화된 접근 방식
- 코드와 관련된 특정 취약점을 간단히 소개하는 내용을 제공
- Prompt 예) start-bug는 수정이 필요한 라인의 시작 지점이며, end-bug는 변경이 필요한 라인의 끝이다. start-bug와 end-bug 경게를 벗어난 코드는 변경되어서는 안된다.
- 변경되면 안된다는 것을 명시해야 LLM이 어느 취약점을 수정할지 알 수 있음 (-> SWE Agent논문에서도 비슷한 정보를 제공)
- 생성된 코드 스니펫과, 정답 코드 사이의 분석 수행
- Edit Distance 분석 : 생성된 코드 스니펫과 올바른 패치 코드 사이의 편집 거리를 계산
- Manual Verification : 생성된 코드 스니펫의 부정확성의 이유를 확인하기 위한 수동 검증
- ChatGPT-4(16.5%), Claude(11.6%)의 성공
- CWE-401의 비율은 올바르게 답변한 데이터 중에서 약 43%와 53%을 차지
(CWE-401 : 메모리 할당은 발생하나 할당 해제에 실패) - CWE-401 취약점을 패치하는 것이 쉬운 주된 이유는 코드가 오직 지역 변수에만 의존하기 때문. C/C++에서 메모리 할당은 변수 이름 앞에 별표(*)가 오는 포인터에 의해 제어. LLM은 포인터를 식별하는 데 주목할 만한 능숙함을 보여줌
- 예시 3) 코드 스니펫은 𝑟𝑡𝑙8𝑥𝑥𝑥𝑢_𝑠𝑢𝑏𝑚𝑖𝑡_𝑖𝑛𝑡_𝑢𝑟𝑏라는 함수
-> 이는 디바이스 드라이버에서 인터럽트 𝑢𝑟𝑏를 제출하는 역할. 이 함수 내에서, 𝑢𝑠𝑏_𝑎𝑙𝑙𝑜𝑐_𝑢𝑟𝑏를 사용하여 𝑢𝑟𝑏 객체 할당 - 𝑢𝑠𝑏_𝑢𝑛𝑎𝑛𝑐ℎ𝑜𝑟_𝑢𝑟𝑏(𝑢𝑟𝑏) 호출 후, 𝑢𝑟𝑏에 할당된 메모리를 해제하지 않는 문제를 인식
- 제안된 수정안은 "start-bug"와 "end-bug" 마커 사이에 𝑢𝑠𝑏_𝑓𝑟𝑒𝑒_𝑢𝑟𝑏(𝑢𝑟𝑏) 함수 삽입
- 명시적으로 할당된 𝑢𝑟𝑏 메모리를 해제하여 CWE-401 메모리 누수 문제를 해결
-> 특정 할당 해제 함수에 대한 정보가 없는데, LLM은 할당된 메모리를 해제하기 위한 적절한 함수(예: 𝑢𝑠𝑏_𝑓𝑟𝑒𝑒_𝑢𝑟𝑏)를 추론함.
Issues in Patching the Code
부정확하게 생성한 범주는 크게 4가지로 나눌 수 있다
- C1: 패치된 코드 컨텍스트 부족: 관련 정보가 부족한 취약점을 포함합니다. 코드 스니펫 자체 내에서 필수적인 정보가 불충분하여 문제가 발생하며, 함수와 구조체 정의와 같은 핵심 정보가 스니펫의 범위를 벗어나 존재.
- C2: 패치된 코드가 새로운 문제를 발생시킴: 이 범주 내에서, 패치된 코드는 처음에 식별된 취약점을 해결하지만 의도치 않게 새로운 문제를 발생시킨다. 이는 취약점을 해결할 때 의도하지 않은 결과가 발생할 수 있음을 강조.
- C3: 취약한 코드 식별의 오류: 이 범주의 취약점은 코드 스니펫 내에서 취약한 부분을 정확히 집어내는데 실패.
-> 취약점의 근본 원인을 정확히 이해하지 못함.
-> 제공된 패치(인풋 데이터)의 문제 : 정확한 부분을 짚어주지 않음 - C4: 코드 이해의 오류: 코드의 기능에 대한 오해, 잘못된 해석으로 인함.
C1: 패치된 코드 컨텍스트 부족
- 코드를 패치하는 데 중요한 정보가 누락.
- 패치된 코드가 제공된 스니펫에 없는 변수, 구조체, 또는 함수에 의존하여 불완전하거나 부정확한 패치가 발생
-> 취약점을 효과적으로 해결하기 위해서는 LLM이 코드 스니펫 내의 모든 관련 정보에 접근할 수 있어야 한다
- 예시 4) 코드 스니펫은 CWE-416으로 분류된 취약점을 보여줌
-> 해제 후 사용. 프로그램이 이미 할당 해제된 메모리에 접근하려고 할 때 발생(메모리 손상, 충돌)- 서버 연결을 처리하는 𝑑𝑒𝑠𝑡𝑟𝑜𝑦_𝑠𝑒𝑟𝑣𝑒𝑟_𝑐𝑜𝑛𝑛𝑒𝑐𝑡 함수를 포함
이 함수 내에서, 𝑖𝑟𝑐𝑐𝑜𝑛𝑛 포인터에 할당된 메모리는 𝑢𝑠𝑒𝑟𝑚𝑜𝑑𝑒와 𝑎𝑙𝑡𝑒𝑟𝑛𝑎𝑡𝑒_𝑛𝑖𝑐𝑘 포인터에서 해제 - 𝑔_𝑓𝑟𝑒𝑒_𝑛𝑜𝑡_𝑛𝑢𝑙𝑙 함수 :
- 목적 : 포인터가 NULL이 아닌 경우에 메모리를 해제하는 함수, 이미 할당 해제된 메모리나 초기화되지 않은 포인터를 해제하려고 할 때 발생할 수 있는 잠재적인 크래시나 오류를 방지.
- ChatGPT 방법 :
- CWE-416 취약점을 해결하기 위해 메모리 할당을 해제한 후 𝑢𝑠𝑒𝑟𝑚𝑜𝑑𝑒와 𝑎𝑙𝑡𝑒𝑟𝑛𝑎𝑡𝑒_𝑛𝑖𝑐𝑘를 NULL로 설정하는 해결책을 제안
- 이 해결책의 의도는 해제된 메모리에 접근하는 것과 관련된 잠재적인 문제를 방지하는 것
- 그러나 ChatGPT의 해결책은 이 취약점을 해결하는 데 필요한 올바른 패치와는 차이가 있음.
- 정확한 수정은 𝑖𝑟𝑐𝑐𝑜𝑛𝑛 구조체 내의 𝑠𝑎𝑠𝑙_𝑢𝑠𝑒𝑟𝑛𝑎𝑚𝑒과 𝑠𝑎𝑠𝑙_𝑝𝑎𝑠𝑠𝑤𝑜𝑟𝑑 포인터를 해제하는 것을 예상함
- ChatGPT가 올바른 패치를 제공하지 못한 이유 :
- 코드의 내부 구조(𝑖𝑟𝑐𝑐𝑜𝑛𝑛)에 대한 이해가 제한적이기 때문
- ChatGPT가 코드 스니펫 내에서 제공된 정보에 의존, 관련된 정보, 맥락을 알지 못하기 때문이다.
- 이러한 정보, 맥락을 제공하면..?
- 서버 연결을 처리하는 𝑑𝑒𝑠𝑡𝑟𝑜𝑦_𝑠𝑒𝑟𝑣𝑒𝑟_𝑐𝑜𝑛𝑛𝑒𝑐𝑡 함수를 포함
C2: 패치된 코드가 새로운 문제를 발생
패치된 코드가 처음에 식별된 취약점을 성공적으로 해결하나, 의도치 않게 다른 문제를 발생시킴. 따라서, 코드 패치를 적용할 때 잠재적인 부작용을 신중히 고려하고 철저히 검토해야 함
- 예시 5) 𝑝ℎ𝑝_ℎ𝑡𝑚𝑙_𝑒𝑛𝑡𝑖𝑡𝑖𝑒𝑠 함수 :
𝑠𝑡𝑟 문자열을 처리하고 다른 함수인 𝑝ℎ𝑝_𝑒𝑠𝑐𝑎𝑝𝑒_ℎ𝑡𝑚𝑙_𝑒𝑛𝑡𝑖𝑡𝑖𝑒𝑠_𝑒𝑥를 사용하여 HTML 엔티티 인코딩을 수행하여 특수 문자를 해당하는 HTML 엔티티로 변환
CWE-190과 관련된 취약점이 있어, 𝑛𝑒𝑤_𝑙𝑒𝑛을 다룰 때 잠재적인 정수 오버플로우나 wraparound가 발생할 수 있음- ChatGPT : 𝑛𝑒𝑤_𝑙𝑒𝑛의 값이 허용된 최대 정수 값(𝐼𝑁𝑇_𝑀𝐴𝑋)을 초과하는지 확인함.
이 조건이 참이면, 𝑝ℎ𝑝_𝑒𝑟𝑟𝑜𝑟_𝑑𝑜𝑐𝑟𝑒𝑓 함수(ChatGPT 훈련 데이터셋에서 검색됨)를 사용하여 오류 메시지를 트리거하고 𝐹𝐴𝐿𝑆𝐸를 반환하여 오류 상태를 나타냄- FALSE를 반환하기 전에 𝑟𝑒𝑝𝑙𝑎𝑐𝑒𝑑 포인터를 할당 해제하지 않아 메모리 누수가 발생할 수 있다. 즉 고친 코드에도 결함이 있을 수 있다.
- ChatGPT : 𝑛𝑒𝑤_𝑙𝑒𝑛의 값이 허용된 최대 정수 값(𝐼𝑁𝑇_𝑀𝐴𝑋)을 초과하는지 확인함.
C3: 취약한 코드 식별의 오류 : 주어진 코드 스니펫 내에서 취약한 특정 부분을 정확히 탐지하는데 실패한 것을 의미함
생성된 패치는 문제가 발생한 위치를 정확히 타겟팅하지 못하기 때문에 수정을 잘하지 못함.
- 예 6) 𝑔𝑑𝐼𝑚𝑎𝑔𝑒𝑊𝐵𝑀𝑃𝑃𝑡𝑟 함수 : GD 이미지에서 데이터를 추출하는 것
- CWE-415와 관련된 취약점을 포함(이중 해제 (CWE-415): 프로그램이 같은 메모리 위치를 두 번 이상 해제하려고 할 때 발생
- 𝑔𝑑𝐼𝑂𝐶𝑡𝑥 구조체(𝑜𝑢𝑡)의 부적절한 처리로 인해 잠재적인 이중 해제 오류 발생 가능
- ChatGPT :
- 1. 여러 개의 마커가 있을 때 -> 하나를 주석으로 취급함
- 2. 코드의 복잡한 맥락을 분석할 때 어려움을 겪어 취약점을 포함하는 세그먼트를 정확히 식별하는데 어려움을 겪음
- ChatGPT :
C4: 코드 이해의 오류 : 코드의 내재된 논리와 기능성에 대한 오해된 인식이나 잘못된 해석
- 예 7) 버퍼 오류 (CWE-119): 버퍼의 경계를 벗어난 곳에서 데이터를 읽거나 쓸 때 발생.
TECHNIQUES TO IMPROVE VULNERABILITY REPAIR
LLM에 의한 코드 취약점 분석은 다음과 같은 어려움이 있다.
- 코드 맥락의 복잡한 특성
- 프롬프트 설명의 정확성
- 잠재적 모호성 : 취약점의 텍스트 묘사의 모호성
- 프롬프트의 의존성 : 프롬프트가 포괄적이지 않다면(필요한 지식을 다 제공하지 않는다면) 부정확, 비효과적 코드 생성으로 이어짐
이를 해결하기 위해 additional knowledge가 중요하며 다음 섹션에서 다룸.
Context of Variables
변수의 맥락적 중요성을 이해하는 것은 LLM이 코드 취약점을 패치하는 능력에 있어 핵심적임
- 변수의 범위
- 잠재적 값
- 코드 스니펫 내에서의 사용 패턴을 파악
빨간 박스를 제공하여 효과적으로 해결했다고 하는데 -> 이를 자동화해서 정보를 제공할 방법을 생각해봐야 할 것 같음.
External Functions, Data Structures or Variables
아래 정보를 제공하면 잠재적인 메모리 손상 취약점을 식별하고 안전한 메모리 관리 관행을 준수하는 코드 생성 가능
- 반환 값
- 데이터 타입
- 접근 패턴
Logic of the Vulnerable Code
취약한 코드의 논리와 기능을 이해하는 것은 의미 있는 패치를 생성하는 데 필수적
- 코드의 예상 동작
- 다른 코드 세그먼트의 목적
- 함수의 의도된 흐름을 인식
예를 들어, 예시 6에 해당하는 케이스 스터디 3에서, 𝑔𝑑𝐼𝑚𝑎𝑔𝑒𝑊𝐵𝑀𝑃𝐶𝑡𝑥, _𝑔𝑑𝐼𝑚𝑎𝑔𝑒𝑊𝐵𝑀𝑃𝐶𝑡𝑥, 𝑔𝑑𝐷𝑃𝐸𝑥𝑡𝑟𝑎𝑐𝑡𝐷𝑎𝑡𝑎와 같은 외부 함수에 대한 설명이 필수적이다.