[CVE] CVE 분석 · 작성: 2026-07-20 03:10:34 · 수정: 2026-07-21 23:19:31 · 조회 20
pipe_buffer의 플래그가 명시적으로 초기화되지 않아, splice()로 끌어온 읽기 전용 파일의 페이지 캐시 페이지에 PIPE_BUF_FLAG_CAN_MERGE가 남아있을 수 있고, 이후 일반 write()가 그 플래그를 신뢰해 파일 내용을 권한 검사 없이 직접 덮어쓴다.패치는 copy_page_to_iter_pipe()와 push_pipe()(fs/pipe.c) 두 함수에서 새 파이프 버퍼 슬롯을 준비할 때 플래그 필드를 명시적으로 0으로 리셋하도록 한 줄을 추가하는 것으로 요약된다.
copy_page_to_iter_pipe() — 플래그 미초기화Before
static size_t copy_page_to_iter_pipe(struct pipe_inode_info *pipe,
struct page *page, ...) {
struct pipe_buffer *buf = pipe->bufs + pipe->head % pipe->ring_size;
if (!pipe_full(...)) {
buf->page = page;
buf->ops = &page_cache_pipe_buf_ops;
buf->offset = offset;
buf->len = bytes;
// buf->flags 를 새로 세팅하지 않음 — 이 슬롯을 이전에 쓰던 값이 그대로 남아있음
pipe->head++;
}
}
이 슬롯이 과거에 일반 파이프 쓰기(CAN_MERGE 플래그가 세팅되는 경로)로 쓰였던 적이 있다면, 그 플래그 값이 그대로 남아있는 상태에서 파일의 페이지 캐시 페이지를 가리키는 용도로 재사용된다.
push_pipe() — 동일 패턴Before
static int push_pipe(struct pipe_inode_info *pipe, unsigned flags, ...) {
struct pipe_buffer *buf = pipe->bufs + pipe->head % pipe->ring_size;
buf->ops = &default_pipe_buf_ops;
buf->page = page;
// 마찬가지로 buf->flags를 초기화하지 않는 경로가 존재
}
After (패치)
static size_t copy_page_to_iter_pipe(...) {
struct pipe_buffer *buf = pipe->bufs + pipe->head % pipe->ring_size;
if (!pipe_full(...)) {
buf->page = page;
buf->ops = &page_cache_pipe_buf_ops;
buf->offset = offset;
buf->len = bytes;
buf->flags = 0; // 핵심 패치: 새 슬롯의 플래그를 명시적으로 리셋
pipe->head++;
}
}
새로 채워지는 pipe_buffer 슬롯의 flags를 항상 0으로 초기화해서, 이전 사용 흔적(stale state)이 다음 컨텍스트로 넘어오지 못하게 막는 게 패치의 전부다.
CAN_MERGE 플래그가 세팅되도록 데이터를 채웠다가 전부 읽어 비움(플래그 오염 준비)splice()로 그 파일의 페이지 캐시 페이지를 파이프로 끌어옴 — 이 슬롯에 앞서 오염시킨 CAN_MERGE 플래그가 남아있음write()로 원하는 내용을 씀 — 커널이 CAN_MERGE를 보고 새 페이지 대신 원본 파일의 페이지 캐시를 직접 덮어씀write() 접근 제어를 한 번도 거치지 않고 내용이 변경된 것/etc/passwd 변조 → 즉시 root 권한 상승: UID 0 계정을 추가하거나 기존 계정 필드를 조작하면 별도 권한 상승 취약점 없이 root 획득