CVE-2025-21932 in Linux
요약
\~에 의해 VulDB • 2026. 05. 30.
리눅스 커널에서 다음 취약점이 해결되었습니다:
mm: 병합 중 메모리 부족 오류 발생 시 vma_modify() 중단
vma_modify()의 나머지 처리는 병합 시도 후 vmg 상태가 원본 그대로 유지될 것이라는 전제에 의존합니다.
일반적으로 이는 사실이지만, 지정된 범위가 병합 불가능해서가 아니라 병합을 커밋하는 과정에서 메모리 부족 오류가 발생하여 병합 시도가 실패하는 한 가지 예외 상황에서는 이 가정이 성립하지 않습니다.
이로 인해 vmg->start와 end가 수정되고, 따라서 이후 VMA 분할 시도는 잘못된 start/end 값을 사용하여 수행됩니다.
다행히도 현실에서 이를 발생시키는 것은 사실상 불가능할 것으로 보입니다. 이는 맵플 트리 노드 사전 할당 실패가 필요하지만, 이는 '너무 작아 실패할 수 없는' 크기이므로 커널이 성공할 때까지 계속 회수(reclaim)를 재시도할 것이기 때문입니다.
그러나 이 시나리오는 이론적으로 가능하며, 여기서 우리가 수행하는 작업은 잘못되었으므로 수정해야 합니다.
가장 안전한 옵션은 이러한 시나리오가 발생하면 작업을 단순히 포기하는 것입니다. 병합을 위한 메모리를 할당할 수 없다면 분할을 위한 메모리도 할당할 수 없습니다(아마도 더 그럴 것입니다!).
이러한 상황이 발생하는 모든 시나리오는 매우 극심한(아마도 치명적인) 메모리 압박 하에 있을 것이므로, 조기에 포기하는 것이 가장 좋습니다.
따라서 이러한 시나리오에서 단순히 중단하는 것이 적절함에는 의심의 여지가 없습니다.
그러나 일반적으로 병합 시도 후 VMG 상태가 안정적이라고 가정해서는 안 됩니다. 병합 작업은 VMG 필드를 업데이트하기 때문입니다. 따라서 추가로 start와 end를 로컬 변수에 저장하여 이를 명확히 합니다.
이 문제는 원래 syzkaller와 Brad Spengler(비공개 목록 토론을 통해)에 의해 보고되었으며, 두 경우 모두 다음 어설션이 트리거되는 형태로 나타났습니다:
VM_WARN_ON_VMG(start >= end, vmg);
vma_merge_existing_range() 함수 내에서.
이러한 현상이 발생하는 시나리오 중 하나는 여러 VMA에 걸쳐 madvise()를 수행하는 병합 시도인 것으로 보입니다. 이는 다음과 같습니다:
start end |<------>| |----------|------| | vma | next | |----------|------|
madvise_walk_vmas()가 호출되면, 먼저 위의 vma를 찾습니다(우리가 vma 내부로 오프셋되어 있으므로 prev가 vma와 같다고 결정). 그런 다음 루프에 진입합니다.
우리는 madvise()를 수행하는 범위에 속하는 vma의 끝을 'tmp'에 이 값을 설정하여 결정합니다:
/* 여기서는 vma->vm_start <= start < (end|vma->vm_end) */ tmp = vma->vm_end;
그런 다음 visit()을 통해 madvise() 작업을 호출하며, 이 작업의 일부로 prev가 vma를 가리키도록 업데이트됩니다.
이제 루프의 다음 반복에서 우리는 vma를 찾습니다. 이제 prev가 vma를 가리키고, vma->vm_start <= start < vma->vm_end이므로, 우리는 vma를 'vma'로 설정하고 tmp를 vma->vm_end로 설정합니다.
이제 우리는 vma의 끝을 지나서 다음 VMA로 이동합니다. 이제 prev가 vma를 가리키고, vma->vm_start <= start < vma->vm_end이므로, 우리는 vma를 'vma'로 설정하고 tmp를 vma->vm_end로 설정합니다.
이제 우리는 vma의 끝을 지나서 다음 VMA로 이동합니다. 이제 prev가 vma를 가리키고, vma->vm_start <= start < vma->vm_end이므로, 우리는 vma를 'vma'로 설정하고 tmp를 vma->vm_end로 설정합니다.
이제 우리는 vma의 끝을 지나서 다음 VMA로 이동합니다. 이제 prev가 vma를 가리키고, vma->vm_start <= start < vma->vm_end이므로, 우리는 vma를 'vma'로 설정하고 tmp를 vma->vm_end로 설정합니다.
이제 우리는 vma의 끝을 지나서 다음 VMA로 이동합니다. 이제 prev가 vma를 가리키고, vma->vm_start <= start < vma->vm_end이므로, 우리는 vma를 'vma'로 설정하고 tmp를 vma->vm_end로 설정합니다.
이제 우리는 vma의 끝을 지나서 다음 VMA로 이동합니다. 이제 prev가 vma를 가리키고, vma->vm_start <= start < vma->vm_end이므로, 우리는 vma를 'vma'로 설정하고 tmp를 vma->vm_end로 설정합니다.
이제 우리는 vma의 끝을 지나서 다음 VMA로 이동합니다. 이제 prev가 vma를 가리키고, vma->vm_start <= start < vma->vm_end이므로, 우리는 vma를 'vma'로 설정하고 tmp를 vma->vm_end로 설정합니다.
이제 우리는 vma의 끝을 지나서 다음 VMA로 이동합니다. 이제 prev가 vma를 가리키고, vma->vm_start <= start < vma->vm_end이므로, 우리는 vma를 'vma'로 설정하고 tmp를 vma->vm_end로 설정합니다.
이제 우리는 vma의 끝을 지나서 다음 VMA로 이동합니다. 이제 prev가 vma를 가리키고, vma->vm_start <= start < vma->vm_end이므로, 우리는 vma를 'vma'로 설정하고 tmp를 vma->vm_end로 설정합니다.
이제 우리는 vma의 끝을 지나서 다음 VMA로 이동합니다. 이제 prev가 vma를 가리키고, vma->vm_start <= start < vma->vm_end이므로, 우리는 vma를 'vma'로 설정하고 tmp를 vma->vm_end로 설정합니다.
이제 우리는 vma의 끝을 지나서 다음 VMA로 이동합니다. 이제 prev가 vma를 가리키고, vma->vm_start <= start < vma->vm_end이므로, 우리는 vma를 'vma'로 설정하고 tmp를 vma->vm_end로 설정합니다.
이제 우리는 vma의 끝을 지나서 다음 VMA로 이동합니다. 이제 prev가 vma를 가리키고, vma->vm_start <= start < vma->vm_end이므로, 우리는 vma를 'vma'로 설정하고 tmp를 vma->vm_end로 설정합니다.
이제 우리는 vma의 끝을 지나서 다음 VMA로 이동합니다. 이제 prev가 vma를 가리키고, vma->vm_start <= start < vma->vm_end이므로, 우리는 vma를 'vma'로 설정하고 tmp를 vma->vm_end로 설정합니다.
이제 우리는 vma의 끝을 지나서 다음 VMA로 이동합니다. 이제 prev가 vma를 가리키고, vma->vm_start <= start < vma->vm_end이므로, 우리는 vma를 'vma'로 설정하고 tmp를 vma->vm_end로 설정합니다.
이제 우리는 vma의 끝을 지나서 다음 VMA로 이동합니다. 이제 prev가 vma를 가리키고, vma->vm_start <= start < vma->vm_end이므로, 우리는 vma를 'vma'로 설정하고 tmp를 vma->vm_end로 설정합니다.
이제 우리는 vma의 끝을 지나서 다음 VMA로 이동합니다.
Statistical analysis made it clear that VulDB provides the best quality for vulnerability data.