syzbot 버그픽스 기여 방법

2026. 4. 22. 03:16·Linux/start_contribute()

Syzbot을 위한 환경 설정

git clone 리눅스 커널 가져오기

버그 찾기

c repro가 있는 것, open되어 있는 것 중에서 찾고, out of bounds를 위주로 찾아보자!! 이게 쉽다. 그리고 로컬 QEMU환경에서 버그를 재현해야한다.

Kernel Build 하기

리포트에 명시되어 있는, config를 활용하여 commit hash로 checkout한다. 그리고 이 상태에서 빌드한다.

  1. make olddefconfig : 커널의 루트에 .config작성하고 적용
  2. make -j$(nproc) bzImage : bzImage로 빌드

환경 만들기

  1. syzkaller clone해오기
    1. 파일 시스템 이미지 준비 (Debian Trixie): trixie.img
    2. 엔진: bzImage
    3. 차체: trixie.img
    4. syzkaller/tools에 ./create-image.sh 를 실행해서 trixie.img를 만든다
  2. repro.c다운 받아서 컴파일: gcc -pthread -static -o repro repro.c

QEMU 실행 스크립트

bzImage와 trixie.img는 경로를 맞게 설정해준다.

qemu-system-x86_64 \
  -m 4G \
  -smp 2 \
  -kernel ./arch/x86/boot/bzImage \
  -append "console=ttyS0 root=/dev/sda earlyprintk=serial nokaslr panic=1" \
  -drive file=./trixie.img,format=raw \
  -net user,host=10.0.2.10,hostfwd=tcp::10022-:22 -net nic \
  -enable-kvm \
  -nographic

repro실행파일 로컬에서 전송

scp -i ./trixie.id_rsa -P 10022 ../../crepro/repro root@localhost:/root/ 를 사용하여 repro를 전송하기
그리고 ./repro를 실행하여 버그재현
여기서 trixie.id_rsa는 키이다.


실제 버그 분석

executing program
[  337.735312][ T9986] loop0: detected capacity change from 0 to 32768
[  337.791047][ T9986] MetaData crosses page boundary!!
[  337.791053][ T9986] lblock = 8bffffffff, size  = 1154220032
[  337.791066][ T9986] CPU: 1 UID: 0 PID: 9986 Comm: repro Not tainted 6.19.0-03606-g192c0159402e #1 PREEMPT_{RT,(full)} 
[  337.791076][ T9986] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[  337.791082][ T9986] Call Trace:
[  337.791085][ T9986]  <TASK>
[  337.791090][ T9986]  dump_stack_lvl+0x164/0x1f0
[  337.791114][ T9986]  __get_metapage+0x6d1/0x1090
[  337.791131][ T9986]  dtReadFirst+0x396/0x990
[  337.791141][ T9986]  ? __pfx_jfs_readdir+0x10/0x10
[  337.791151][ T9986]  jfs_readdir+0x5ce/0x4780
[  337.791166][ T9986]  ? __lock_acquire+0x45c/0x25f0
[  337.791178][ T9986]  ? __pfx_jfs_readdir+0x10/0x10
[  337.791191][ T9986]  ? do_raw_spin_lock+0x128/0x270
[  337.791205][ T9986]  ? __pfx_rwbase_write_lock+0x10/0x10
[  337.791217][ T9986]  ? __pfx___might_resched+0x10/0x10
[  337.791229][ T9986]  ? wrap_directory_iterator+0x52/0xe0
[  337.791242][ T9986]  ? __pfx_jfs_readdir+0x10/0x10
[  337.791252][ T9986]  wrap_directory_iterator+0xa8/0xe0
[  337.791264][ T9986]  iterate_dir+0x2ab/0xb10
[  337.791277][ T9986]  __x64_sys_getdents64+0x13b/0x2c0
[  337.791289][ T9986]  ? __pfx___x64_sys_getdents64+0x10/0x10
[  337.791300][ T9986]  ? __x64_sys_openat+0x141/0x200
[  337.791311][ T9986]  ? __pfx_filldir64+0x10/0x10
[  337.791326][ T9986]  do_syscall_64+0x112/0xf80
[  337.791337][ T9986]  ? irqentry_exit+0x122/0x7a0
[  337.791349][ T9986]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
[  337.791358][ T9986] RIP: 0033:0x4203ad
[  337.791368][ T9986] Code: b3 66 2e 0f 1f 84 00 00 00 00 00 66 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 c0 ff ff ff f7 d8 64 89 01 48
[  337.791375][ T9986] RSP: 002b:00007ffdd842a9a8 EFLAGS: 00000213 ORIG_RAX: 00000000000000d9
[  337.791383][ T9986] RAX: ffffffffffffffda RBX: 0000000000000001 RCX: 00000000004203ad
[  337.791388][ T9986] RDX: 0000000000001000 RSI: 0000200000000f80 RDI: 0000000000000006
[  337.791393][ T9986] RBP: 00007ffdd842a9c0 R08: 00007ffdd842a9c0 R09: 00007ffdd842a9c0
[  337.791398][ T9986] R10: 00007ffdd842a9c0 R11: 0000000000000213 R12: 00007ffdd842ab48
[  337.791402][ T9986] R13: 00007ffdd842ab58 R14: 00000000004b2f08 R15: 0000000000000001
[  337.791413][ T9986]  </TASK>
[  337.791416][ T9986] bread failed!

repro로 버그를 재현한 결과, 위와 같은 콜 스택이 나왔다.
여기서 확인할 수 있는건, __get_metapage에서 종료가 되었고, 이 함수를 유발시킨 함수는 dtReadFirst라는 함수이다. 여기서 보면 dtReadFirst+0x396/0x990 이렇게 나와있는데, 이건 dtReadFirst함수로부터 0x396바이트가 떨어진 곳에서 에러가 발생했다는 뜻이다.
그러면 이 관측 결과를 토대로, 어디서 어떤 코드에서 에러가 발생했는지 알 수 있다.

버그가 어디서 발생했는지?

faddr2line으로 버그가 어떤 코드에서 발생했는지 확인해보자.
./scripts/faddr2line vmlinux dtReadFirst+0x396 실행

dtReadFirst+0x396/0x990:
dtReadFirst at fs/jfs/jfs_dtree.c:3079 (discriminator 2)

이렇게 하면, 위치가 jfs_dtree.c의 3079번 줄에서 에러가 났음을 알 수 있다.

struct metapage *__get_metapage(struct inode *inode, unsigned long lblock,
				unsigned int size, int absolute,
				unsigned long new)
{
	int l2BlocksPerPage;
	int l2bsize;
	struct address_space *mapping;
	struct metapage *mp = NULL;
	struct folio *folio;
	unsigned long page_index;
	unsigned long page_offset;

	jfs_info("__get_metapage: ino = %ld, lblock = 0x%lx, abs=%d",
		 inode->i_ino, lblock, absolute);

	l2bsize = inode->i_blkbits;
	l2BlocksPerPage = PAGE_SHIFT - l2bsize;
	page_index = lblock >> l2BlocksPerPage;
	page_offset = (lblock - (page_index << l2BlocksPerPage)) << l2bsize;
	if ((page_offset + size) > PAGE_SIZE) {
		jfs_err("MetaData crosses page boundary!!");
		jfs_err("lblock = %lx, size  = %d", lblock, size);
		dump_stack();
		return NULL;
	}

여기서, 아까 발생했던 log

[  337.791047][ T9986] MetaData crosses page boundary!!
[  337.791053][ T9986] lblock = 8bffffffff, size  = 1154220032

이 로그가 어디서 발생했는지 알 수 있다.
이 로그가 왜 발생했냐면, if ((page_offset + size) > PAGE_SIZE)에서 걸렸기 때문에 에러가 발생했다는 것을 알 수 있다. 여기서 lblock = 8bffffffff 라는건 말이 안되는 숫자이다.
즉, 인자로 들어온 lblock이 문제가 발생했다는 것이다.

static int dtReadFirst(struct inode *ip, struct btstack * btstack)
{
	int rc = 0;
	s64 bn;
	int psize = 288;	/* initial in-line directory */
	struct metapage *mp;
	dtpage_t *p;
	s8 *stbl;
	struct btframe *btsp;
	pxd_t *xd;

	BT_CLR(btstack);	/* reset stack */

	/*
	 *	descend leftmost path of the tree
	 *
	 * by convention, root bn = 0.
	 */
	for (bn = 0;;) {
		DT_GETPAGE(ip, bn, mp, psize, p, rc);
		if (rc)
			return rc;

		/*
		 * leftmost leaf page
		 */
		if (p->header.flag & BT_LEAF) {
			/* return leftmost entry */
			btsp = btstack->top;
			btsp->bn = bn;
			btsp->index = 0;
			btsp->mp = mp;

			return 0;
		}

		/*
		 * descend down to leftmost child page
		 */
		if (BT_STACK_FULL(btstack)) {
			DT_PUTPAGE(mp);
			jfs_error(ip->i_sb, "btstack overrun\\n");
			BT_STACK_DUMP(btstack);
			return -EIO;
		}
		/* push (bn, index) of the parent page/entry */
		BT_PUSH(btstack, bn, 0);

		/* get the leftmost entry */
		stbl = DT_GETSTBL(p);

		if (stbl[0] < 0) {
			DT_PUTPAGE(mp);
			jfs_error(ip->i_sb, "stbl[0] out of bound\\n");
			return -EIO;
		}

		xd = (pxd_t *) & p->slot[stbl[0]];

		/* get the child page block address */
		bn = addressPXD(xd);
		psize = lengthPXD(xd) << JFS_SBI(ip->i_sb)->l2bsize;

		/* unpin the parent page */
		DT_PUTPAGE(mp);
	}
}

문제가 발생했던 부분은, DT_GETPAGE에서 인자로 전달되는 bn에 문제가 생겼었던 것이고, bn은 bn = addressPXD(xd)에 의해서 얻게된다. bn에 쓰레기값이 그대로 전달되기 때문에 에러가 발생했던 것이다.
따라서, bn = addressPXD(xd)로 얻은 bn을 검사하기만 하면 에러를 해결할 수 있다.

bn = addressPXD(xd);
if (bn >= JFS_SBI(ip->i_sb)->bmap->db_bmap.dn_mapsize) {
	jfs_error(ip->i_sb, "invalid block number\\n");
	return -EFSCORRUPTED;
}

를 추가한다.
그리고 다시 재빌드를 한 후, repro를 돌려봤더니 커널 패닉이 발생하지 않는 것을 알 수 있었다.
그대로 syzbot에게 이 패치를 제출한다.


누가 이미 제출했다

확인해보니, 이미 누가 이 패치를 그새 제출했다… 또 다른 버그를 찾아서 고쳤으나 1달전에 이미 누가 제출했다…ㅠ

'Linux > start_contribute()' 카테고리의 다른 글

syzbot - memory leak in gsm_activate_mux  (0) 2026.04.24
커널 기여: race condition 가능성 해결  (0) 2026.02.01
커널 기여 근황  (0) 2026.01.27
리눅스 커널 2번째 기여: 수동 메모리 정렬 연산 PTR_ALIGN으로 최적화  (2) 2026.01.12
첫 리눅스 커널 기여  (4) 2025.12.22
'Linux/start_contribute()' 카테고리의 다른 글
  • syzbot - memory leak in gsm_activate_mux
  • 커널 기여: race condition 가능성 해결
  • 커널 기여 근황
  • 리눅스 커널 2번째 기여: 수동 메모리 정렬 연산 PTR_ALIGN으로 최적화
Minu Jin
Minu Jin
정보의 바다
  • Minu Jin
    뇌 구조가 바이너리
    Minu Jin
  • 전체
    오늘
    어제
    • 분류 전체보기
      • C프로그래밍
        • 오류해결
        • 개인 공부
        • Programming Lab(학교수업)
        • MemoryTracker
      • C++
        • 개인 공부
      • 자료구조(Data Structure)
      • ARM arch
        • Cortex-M
        • FreeRTOS
      • 컴퓨터 공학(Computer Science)
        • OS
        • 컴퓨터 구조
      • Qualcomm 기업과제
      • Linux
        • start_contribute()
        • start_analyse()
      • Web
      • 똥글
      • 백준
      • Git 학습
        • 오류해결
        • 학습중
      • Python
        • 오류해결
        • 개인 공부
  • 블로그 메뉴

    • 태그
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    피보나치
    INIT
    리눅스
    arm
    rubikpi3
    소수
    rubik pi
    이진 트리
    백준
    앤드류모튼
    시스템콜
    포인터
    드라이버 분석
    스택
    Git
    파일 입출력
    버퍼
    Branch
    자료구조
    순환
    커널 기여
    토발즈
    커널
    commit
    C++
    yolo
    Qualcomm
    파이썬
    동적메모리
    c언어
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Minu Jin
syzbot 버그픽스 기여 방법
상단으로

티스토리툴바