🇰🇷 한글 설명
Linux 커널의 Btrfs 파일 시스템에서 로그 재현(log replay) 시 inode 크기가 올바르게 업데이트되지 않는 취약점입니다. 특정 상황에서 파일 크기를 0으로 줄인 후 하드 링크를 생성하고 전원이 차단되면, 재부팅 후 파일 크기가 줄어들기 전 상태로 유지되는 데이터 불일치 문제가 발생합니다.
💡 쉬운 설명
컴퓨터가 갑자기 꺼졌을 때 파일을 복구하는 과정에서 파일 크기를 잘못 계산하는 오류입니다. 이로 인해 실제 파일 내용보다 더 큰 크기로 표시되는 데이터 오류가 발생할 수 있습니다.
🇺🇸 원문 (English)
In the Linux kernel, the following vulnerability has been resolved:
btrfs: fix zero size inode with non-zero size after log replay
When logging that an inode exists, as part of logging a new name or
logging new dir entries for a directory, we always set the generation of
the logged inode item to 0. This is to signal during log replay (in
overwrite_item()), that we should not set the i_size since we only logged
that an inode exists, so the i_size of the inode in the subvolume tree
must be preserved (as when we log new names or that an inode exists, we
don't log extents).
This works fine except when we have already logged an inode in full mode
or it's the first time we are logging an inode created in a past
transaction, that inode has a new i_size of 0 and then we log a new name
for the inode (due to a new hardlink or a rename), in which case we log
an i_size of 0 for the inode and a generation of 0, which causes the log
replay code to not update the inode's i_size to 0 (in overwrite_item()).
An example scenario:
mkdir /mnt/dir
xfs_io -f -c "pwrite 0 64K" /mnt/dir/foo
sync
xfs_io -c "truncate 0" -c "fsync" /mnt/dir/foo
ln /mnt/dir/foo /mnt/dir/bar
xfs_io -c "fsync" /mnt/dir
<power fail>
After log replay the file remains with a size of 64K. This is because when
we first log the inode, when we fsync file foo, we log its current i_size
of 0, and then when we create a hard link we log again the inode in exists
mode (LOG_INODE_EXISTS) but we set a generation of 0 for the inode item we
add to the log tree, so during log replay overwrite_item() sees that the
generation is 0 and i_size is 0 so we skip updating the inode's i_size
from 64K to 0.
Fix this by making sure at fill_inode_item() we always log the real
generation of the inode if it was logged in the current transaction with
the i_size we logged before. Also if an inode created in a previous
transaction is logged in exists mode only, make sure we log the i_size
stored in the inode item located from the commit root, so that if we log
multiple times that the inode exists we get the correct i_size.
A test case for fstests will follow soon.
📍 영향받는 시스템
Btrfs 파일 시스템을 사용하는 Linux 커널 버전
⚔️ 공격 벡터
로컬 사용자가 특정 파일 작업(truncate 후 hardlink 생성)을 수행한 뒤 시스템 전원을 강제로 차단하여 데이터 불일치 유도
🛡️ 대응 방법
Btrfs 로그 재현 시 inode의 generation 및 i_size를 올바르게 기록하도록 수정된 최신 Linux 커널로 업데이트
🔧 검증 코드
출력된 커널 버전이 해당 취약점이 수정된 패치 버전 이후인지 확인