🇰🇷 한글 설명
Linux 커널 netrom 모듈의 nr_heartbeat_expiry() 함수에서 SOCK_DESTROY 플래그가 설정된 소켓에 대해 불필요한 sock_hold() 호출로 인한 메모리 누수 발생. 패치 409db27e3a2e에서 도입된 버그로, listening 소켓이 아닌 경우에는 sock_hold()를 호출하면 안 됨.
💡 쉬운 설명
Linux 커널의 네트워크 통신 기능 중 하나에서 메모리를 제대로 해제하지 않아 메모리 누수가 발생합니다. 오래된 연결을 정리할 때 참조 카운트 관리를 잘못하면 메모리가 계속 점유됩니다.
🇺🇸 원문 (English)
In the Linux kernel, the following vulnerability has been resolved:
netrom: Fix a memory leak in nr_heartbeat_expiry()
syzbot reported a memory leak in nr_create() [0].
Commit 409db27e3a2e ("netrom: Fix use-after-free of a listening socket.")
added sock_hold() to the nr_heartbeat_expiry() function, where
a) a socket has a SOCK_DESTROY flag or
b) a listening socket has a SOCK_DEAD flag.
But in the case "a," when the SOCK_DESTROY flag is set, the file descriptor
has already been closed and the nr_release() function has been called.
So it makes no sense to hold the reference count because no one will
call another nr_destroy_socket() and put it as in the case "b."
nr_connect
nr_establish_data_link
nr_start_heartbeat
nr_release
switch (nr->state)
case NR_STATE_3
nr->state = NR_STATE_2
sock_set_flag(sk, SOCK_DESTROY);
nr_rx_frame
nr_process_rx_frame
switch (nr->state)
case NR_STATE_2
nr_state2_machine()
nr_disconnect()
nr_sk(sk)->state = NR_STATE_0
sock_set_flag(sk, SOCK_DEAD)
nr_heartbeat_expiry
switch (nr->state)
case NR_STATE_0
if (sock_flag(sk, SOCK_DESTROY) ||
(sk->sk_state == TCP_LISTEN
&& sock_flag(sk, SOCK_DEAD)))
sock_hold() // ( !!! )
nr_destroy_socket()
To fix the memory leak, let's call sock_hold() only for a listening socket.
Found by InfoTeCS on behalf of Linux Verification Center
(linuxtesting.org) with Syzkaller.
[0]: https://syzkaller.appspot.com/bug?extid=d327a1f3b12e1e206c16
📍 영향받는 시스템
Linux kernel netrom module (5.10.x - 6.x 버전 근처, commit 409db27e3a2e 이후 버전)
⚔️ 공격 벡터
로컬 메모리 소진 공격 (netrom 소켓 연결 생성/해제 반복)
🛡️ 대응 방법
Linux 커널을 메모리 누수 수정이 적용된 최신 버전으로 업데이트
🔧 검증 코드
커널 소스에서 해당 패턴이 검출되지 않으면 패치됨. 현재 커널 버전이 취약점 수정 이후 버전인지 확인
grep -r 'sock_hold.*SOCK_DESTROY' /usr/src/linux* 2>/dev/null || uname -r | grep -E '^[0-9]+\.[0-9]+'