마지막으로 멈춘 함수가 최초 원인인 것은 아닙니다
잘못된 포인터를 구조체에 저장한 뒤 한참 지나 다른 함수가 그 값을 읽다가 fault가 날 수 있습니다. 따라서 현재 PC의 함수만 고치면 앞선 메모리 손상을 놓칠 수 있습니다. 부팅부터 발생 직전까지의 첫 경고, allocator 보고, 종료·재사용 경로를 함께 봐야 합니다.
| 로그의 정보 | 알 수 있는 것 | 그것만으로 단정할 수 없는 것 |
|---|---|---|
| PC / RIP | 예외·보고 시점의 명령 위치 | 그 함수가 최초로 메모리를 망가뜨렸다는 결론 |
| LR / return address | 연결된 호출·복귀 위치의 단서 | 전체 call stack이 완전하고 손상되지 않았다는 보장 |
| fault address | 실패한 접근 주소의 단서 | 항상 코드 실행 주소와 같은 값이라는 결론 |
| stack trace | 해당 시점의 호출 경로 단서 | 모든 CPU에서 앞서 일어난 이벤트의 시간 순서 |
symbol을 풀 때에는 실제 사용한 vmlinux·module·빌드 설정과 맞춰야 합니다. 최적화·inlining·KASLR·module 적재 주소를 고려하지 않고 다른 빌드의 주소를 소스 줄에 대입하면 잘못된 위치를 지목할 수 있습니다. 커널 오류 위치 조사
QEMU 연결과 빌드 성공을 따로 확인합니다
QEMU로 커널을 멈춰 관찰할 때는 연결 준비와 게스트 실행을 구분합니다. -s는 TCP 1234의 GDB 스텁을 열고, -S는 GDB의 continue 전까지 게스트 CPU를 정지시킵니다. 접속 기능만 켜서는 부팅이 멈추지 않습니다. 아래 x86-64 예제는 접속 주소를 명시하려고 -s 대신 -gdb tcp:127.0.0.1:1234를 사용합니다. QEMU GDB 연결과 실행 제어
심볼이 포함된 vmlinux와 부팅할 bzImage는 같은 커널 빌드에서 준비합니다. 경로는 자신의 빌드 결과로 바꾸고, 게스트 아키텍처에 맞는 initramfs와 실행 가능한 /init도 갖춰야 합니다. nokaslr는 주소 재배치를 제한하는 실습용 부팅 설정입니다. 최적화로 일부 변수는 보이지 않을 수 있습니다. lx-* 도움 명령은 별도로 CONFIG_GDB_SCRIPTS와 make scripts_gdb 준비가 필요합니다. Linux 6.18 GDB 준비 조건
qemu-system-x86_64 -m 512M \
-kernel /path/to/kernel-build/arch/x86/boot/bzImage \
-initrd /path/to/initramfs.cpio.gz \
-append "console=ttyS0 nokaslr" \
-nographic -no-reboot \
-gdb tcp:127.0.0.1:1234 -S
별도 터미널에서 같은 빌드의 심볼을 연 뒤 연결합니다. 중단점의 함수 이름은 해당 빌드에 실제로 존재하는지 확인합니다. 시작 직후 출력이 없다는 사실만으로 부팅 실패라고 판단하지 않습니다.
gdb /path/to/kernel-build/vmlinux
(gdb) target remote 127.0.0.1:1234
(gdb) break kernel_clone
(gdb) continue
GDB 스텁에는 인증·암호화가 없습니다. loopback 제한은 같은 호스트의 다른 사용자를 인증하지도 않습니다. 신뢰할 수 있는 격리 실습 환경에서만 사용하고, 공유 호스트에서는 적절한 권한의 Unix socket 등으로 접속 주체도 제한합니다. QEMU GDB 보안 조건
로그 저장 성공과 커널 빌드 성공은 다릅니다
Bash에서 pipefail을 켜지 않은 make bzImage 2>&1 | tee build.log의 $?는 마지막 명령인 tee의 종료 상태입니다. 로그 파일이 생겼거나 tee가 성공해도 make의 성공을 뜻하지 않습니다. 각 명령을 구분하려면 파이프라인 직후, 다른 명령보다 먼저 status=("${PIPESTATUS[@]}")로 배열을 보관하고 ${status[0]}과 ${status[1]}을 확인합니다. pipefail은 실패를 묶어 확인할 때 유용하지만, 반환값은 가장 오른쪽의 0이 아닌 상태라서 항상 make의 상태와 같지는 않습니다. GNU Bash 파이프라인 종료 상태 PIPESTATUS
이 보충은 Arul mathi의 커널 관찰 자료를 계기로 공식 문서의 준비 조건을 정리한 것입니다. 첨부 14쪽 전체를 확인하거나 위 QEMU·GDB 명령을 실행해 재현한 결과는 아닙니다. 함수 중단점에 도달한 사실과 오류의 최초 원인을 입증한 사실도 구분해야 합니다. 더 자세한 절차는 Linux GDB 참고문서로 이어집니다.
잘못된 접근 검출과 특정 필드의 값 변경 추적은 다릅니다
나중에 NULL이 된 필드를 발견해도 값을 바꾼 명령은 아직 모릅니다. 2018년 Kernelnewbies의 bio_vec.bv_page 토론은 KASAN과 특정 주소의 하드웨어 중단점을 서로 다른 단서로 제안했습니다. KASAN은 범위 밖 접근·해제 후 접근 같은 메모리 안전 오류를 찾지만, 살아 있는 객체의 유효한 필드에 논리적으로 잘못된 값을 쓰는 경우까지 모두 검출하지는 않습니다. 주소 감시를 쓰려면 대상 CPU·커널 디버깅 경로의 지원, 감시 크기·개수와 객체 수명을 별도로 확인합니다. 일반 GDB watch는 값이 바뀔 때 멈추므로 같은 값을 다시 쓰는 모든 접근의 기록과도 다릅니다. 잡힌 스택을 전체 원인의 증명으로 확대하지 않습니다.
English note
A field found NULL later does not identify the modifying instruction. A 2018 Kernelnewbies discussion of bio_vec.bv_page suggested KASAN and an address-specific hardware breakpoint as different clues. KASAN targets memory-safety errors such as out-of-bounds and use-after-free, not every logically incorrect value written into a valid field of a live object. Address monitoring also requires target and kernel-debug-path support, supported widths and counts, and the object’s lifetime. Ordinary GDB watch stops on a changed value, not every same-value write. An observed stack is not the complete root cause.
CONFIG 이름이 선택하는 검사 범위도 읽습니다
#if defined(CONFIG_KASAN) && defined(CONFIG_KASAN_STACK)
/* 이 빌드에서 stack 검사와 관련된 코드가 포함됩니다. */
#endif이 예에서 CONFIG_KASAN은 커널의 메모리 오류 검사 기능 설정이고 CONFIG_KASAN_STACK은 해당 빌드의 stack instrumentation 관련 설정입니다. 두 매크로가 정의됐는지는 전처리 단계의 판단입니다. CPU가 실행 중 현재 stack이 안전한지 이 #if로 검사하는 것이 아닙니다. v6.18.37에서 KASAN_STACK은 KASAN_GENERIC 또는 KASAN_SW_TAGS에 의존합니다. hardware tag 방식의 지원 범위와 별도로 읽어야 합니다.
KASAN에는 generic, software tag, hardware tag 방식이 있고 지원 아키텍처·메모리 영역·비용이 다릅니다. ARM64의 hardware tag 방식은 MTE 지원을 사용합니다. “KASAN을 켰으므로 모든 메모리와 모든 접근을 같은 방식으로 검사한다”고 읽으면 안 됩니다. KASAN mode와 지원 범위 KASAN_STACK의 설정 조건
| 보고에서 찾을 부분 | 읽는 방법 |
|---|---|
| 오류 종류와 접근 크기 | 범위 밖 접근인지 해제 후 접근인지, read인지 write인지부터 구분합니다. |
| 잘못된 접근의 stack | 오류가 드러난 소비 경로를 봅니다. |
| 할당·해제 stack | 그 객체를 누가 만들고 언제 수명을 끝냈는지 봅니다. |
| 객체 내부 offset | 정상 객체 경계와 잘못 접근한 위치를 비교합니다. |
ftrace는 시간 관계를 좁히는 도구입니다
- 요청 제출
요청 번호와 시점을 기록합니다.
queue에 들어간 시점을 확인합니다.
- 실행 시작
worker·thread가 실제 CPU에서 시작한 시점을 봅니다.
작업 중 대기 구간을 확인합니다.
- 장치 또는 상대의 완료
interrupt·callback·reply 시점과 연결합니다.
소비자가 결과를 받는 시점을 확인합니다.
- 응답
전체 latency를 계산하고 어느 구간이 긴지 나눕니다.
화살표는 한 요청의 인과 관계입니다. trace의 모든 줄이 서로 같은 요청이라는 뜻은 아니므로 식별자·CPU·task 정보를 함께 봅니다.
현대 Linux에서는 tracefs를 통해 tracing 인터페이스를 제공하며 보통 /sys/kernel/tracing을 사용합니다. 예전 글의 /sys/kernel/debug/tracing 경로도 구성에 따라 보이지만 debugfs 하나만 외워 모든 시스템에 적용하지 않습니다. trace 설정과 기록은 성능에 영향을 줄 수 있으므로 필요한 event·filter·구간으로 범위를 좁혀야 합니다. ftrace와 tracefs
재현 조건과 증거를 함께 남깁니다
- kernel commit·설정·toolchain과 사용한 firmware 버전을 기록합니다.
- 최초 오류 로그와 뒤따르는 오류를 시간 순서로 보존합니다.
- 정상 경로와 실패 경로의 요청·응답·취소·remove 순서를 비교합니다.
- KASAN·lockdep·ftrace 등 도구가 검출한 사실과 원인 추정을 나누어 적습니다.
- 수정 후에는 같은 재현 조건에서 원래 오류와 종료·timeout 경로까지 확인합니다.
보고에 함수 이름이 있다는 이유로 코드를 지우거나 delay를 넣는 것으로 끝내지 않습니다. 어떤 상태가 먼저 준비돼야 하고 누가 그 상태의 수명을 끝내는지 설명할 수 있어야 합니다.
Slab 증가, kmemleak 후보, 실제 누수를 구분합니다
slabtop에서 객체 수가 늘었다고 바로 누수로 판정하지 않습니다. cache 유지, 아직 끝나지 않은 요청과 정상 수명도 원인일 수 있습니다. 같은 부하를 반복한 뒤 유휴 상태에서 회수되는지, 어떤 할당·해제 경로와 객체가 누적되는지 연결합니다. KASAN의 잘못된 접근 검출과 누수 조사는 서로 다른 문제입니다.
CONFIG_DEBUG_KMEMLEAK와 debugfs를 준비한 검사 커널은 /sys/kernel/debug/kmemleak에서 참조를 찾지 못한 객체 후보를 보고합니다. 아래는 권한과 대상 환경이 갖춰진 경우의 검사 예시이며 이 사이트 작업에서 실행한 명령은 아닙니다.
cat /sys/kernel/debug/kmemleak
echo scan > /sys/kernel/debug/kmemleak
cat /sys/kernel/debug/kmemleak
보고의 할당 stack과 크기를 객체 수명, 실패·timeout·remove 경로의 해제와 대조합니다. 후보가 반드시 실제 누수인 것도, 보고가 없으면 모든 메모리가 안전한 것도 아닙니다. 물리 page 할당과 ioremap 등에는 추적 한계가 있으며 포인터 표현 때문에 놓치거나 잘못 보고할 수 있습니다. clear로 이전 후보 표시를 바꾸기 전에는 원래 보고를 보존합니다. 후보를 지운 것을 누수 수정으로 세지 않습니다.
기준: Linux Kernel Memory Leak Detector. 검출 도구 설정·성능 부담은 테스트 환경에서 먼저 검토합니다.
English summary. Growing slab usage is not proof of a leak. Kmemleak reports unreferenced candidates with coverage and pointer-discovery limits. Preserve reports and trace allocation/free lifetimes; clearing a report is not a fix.
trace-cmd 3.4: 인자 정보와 이전 부팅 기록의 조건
함수 주소만 기록하면 호출의 대상은 보여도 인자 의미를 복원하기 어렵습니다. trace-cmd v3.4의 기록 경로는 function tracer의 func-args 또는 function_graph의 funcgraph-args 상태를 확인해 BTF를 출력에 덧붙입니다. trace.dat v7 형식의 BTF_FILE은 /sys/kernel/btf/vmlinux 자료를 보존하며 인자 이름 해석에 사용됩니다. 해당 tracer·옵션과 BTF를 제공하는 커널 및 호환 도구가 필요합니다. trace-cmd 설치만으로 모든 커널 함수 인자가 기록되는 것은 아닙니다.
같은 버전의 extract 경로는 persistent instance의 last_boot_info를 읽고 출력에 이전 부팅 정보와 모듈 자료를 남깁니다. libtraceevent의 매핑 처리는 이전 부팅의 커널·모듈 주소 차이를 심벌 해석에 반영합니다. 따라서 기록과 대응하는 커널 빌드·심벌·모듈 정보를 함께 보존해야 하며, 인자 이름이 표시됐다는 사실만으로 사건의 최초 원인을 단정하지 않습니다.
커널의 persistent buffer 설명은 다음 부팅까지 같은 메모리 영역과 내용이 보존되는 조건을 요구합니다. 일반적인 휘발성 trace를 소급 복원하는 기능이 아닙니다. 메모리가 덮이거나 검증에 실패하면 기록을 잃을 수 있고, 커널 버전이 바뀌면 레이아웃도 달라질 수 있습니다. 여기서는 부팅 설정·추적 설정을 변경하거나 장애를 재현하지 않았습니다.
English summary. Trace-cmd 3.4 can preserve BTF and previous-boot metadata when the kernel provides the required tracing features. Persistent tracing depends on retained memory and compatible metadata; it does not recover ordinary volatile traces after reboot.