🇰🇷 한글 설명
Linux 커널의 OCFS2 파일 시스템에서 refcount 블록의 rl_used 값이 실제 블록 용량을 초과할 때 발생하는 취약점입니다. 조작된 블록을 통해 배열 범위를 벗어난 메모리 접근(Out-of-bounds) 및 memmove()를 통한 메모리 오염이 가능합니다.
💡 쉬운 설명
파일 시스템의 데이터 기록 공간이 실제 크기보다 훨씬 크다고 속여서, 컴퓨터의 허용된 메모리 범위를 넘어서 데이터를 읽거나 쓰는 보안 결함입니다. 공격자가 조작된 파일을 통해 시스템의 중요한 메모리 영역을 망가뜨릴 수 있습니다.
🇺🇸 원문 (English)
In the Linux kernel, the following vulnerability has been resolved:
ocfs2: validate rl_used against rl_count in refcount block validator
ocfs2_find_refcount_rec_in_rl() walks the on-disk refcount record array
with:
for (; i < le16_to_cpu(rb->rf_records.rl_used); i++) {
rec = &rb->rf_records.rl_recs[i];
...
rl_recs[] lives in a single metadata block (4096 bytes on the common
configuration), so its real capacity is fixed by
ocfs2_refcount_recs_per_rb(sb) (247 records for a 4K block with the
16-byte ocfs2_refcount_rec). rl_used and rl_count are both read directly
off disk by ocfs2_validate_refcount_block() and are never checked against
that capacity, nor against each other, before any refcount/reflink/CoW
operation walks the array.
A crafted (or corrupted) refcount block with rl_used == 0xffff makes the
loop above walk far past the end of the block, dereferencing rl_recs[i]
for i up to 65534. The resulting index is then handed to the sibling
ocfs2_insert_refcount_rec(), whose insert-shift does:
if (index < le16_to_cpu(rf_list->rl_used))
memmove(&rf_list->rl_recs[index + 1],
&rf_list->rl_recs[index],
(le16_to_cpu(rf_list->rl_used) - index) *
sizeof(struct ocfs2_refcount_rec));
i.e. a memmove() of up to (0xffff - index) * 16 bytes (~1 MiB) from an
offset already past the block. This is reachable from an ordinary reflink
(FICLONE) against a crafted/corrupted ocfs2 image: attaching an extent
whose cpos sorts past every real record in the leaf forces the lookup to
run off the end instead of returning early on a match. The attacker model
is local: CAP_SYS_ADMIN mounting a crafted or corrupted ocfs2 image, or a
raw write to the block device backing an already-mounted ocfs2 filesystem.
ocfs2_validate_refcount_block() already validates the block's ECC,
signature, rf_blkno and rf_fs_generation, but never rl_count/rl_used
against the block's actual on-disk capacity. This is the same class of
gap that ocfs2_validate_extent_block() (fs/ocfs2/alloc.c) already closes
for the sibling extent-list header, which checks both the record capacity
and the "used" bound before any code walks h_list.l_recs[]:
if (le16_to_cpu(eb->h_list.l_count) != ocfs2_extent_recs_per_eb(sb)) {
rc = ocfs2_error(...);
goto bail;
}
if (le16_to_cpu(eb->h_list.l_next_free_rec) >
le16_to_cpu(eb->h_list.l_count)) {
rc = ocfs2_error(...);
goto bail;
}
Add the equivalent pair of checks to ocfs2_validate_refcount_block():
reject a refcount block whose rl_count does not match the fixed per-block
capacity returned by ocfs2_refcount_recs_per_rb(), and reject rl_used >
rl_count. Both checks are skipped when OCFS2_REFCOUNT_TREE_FL is set,
because in that case the same union bytes hold an ocfs2_extent_list
(rf_list), not the refcount record list (rf_records) -- that layout is
already validated separately by ocfs2_validate_extent_block() when the
referenced extent block is read. This mirrors the existing
"!(rb->rf_flags & OCFS2_REFCOUNT_TREE_FL)" guard used elsewhere in this
file (e.g. ocfs2_get_refcount_rec()) to decide whether rf_records or
rf_list is the live member of the union.
With this in place, a forged rl_used/rl_count is caught at block
validation time (ocfs2_error()), consistent with every other corruption
check in this function, instead of driving an out-of-bounds read in
ocfs2_find_refcount_rec_in_rl() and a subsequent out-of-bounds memmove()
in ocfs2_insert_refcount_rec().
Verified against a crafted image on a v6.19 KASAN (KASAN_GENERIC) build:
replaying the same reflink (FICLONE) reliably hit a KASAN report in
__ocfs2_increase_refcount()/ocfs2_insert_refcount_rec() before this patch,
and triggers no report once ocfs2_validate_refcount_block() rejects the
forged rl_used/rl_count.
📍 영향받는 시스템
Linux kernel (OCFS2 파일 시스템 사용 환경)
⚔️ 공격 벡터
로컬 공격자가 조작된 OCFS2 이미지를 마운트하거나 이미 마운트된 파일 시스템의 블록 장치에 직접 쓰기 작업을 수행하여 FICLONE 명령 실행
🛡️ 대응 방법
Linux 커널을 최신 보안 패치가 적용된 버전으로 업데이트하여 ocfs2_validate_refcount_block()의 검증 로직을 적용해야 함
🔧 검증 코드
출력된 커널 버전이 해당 취약점이 해결된 패치 버전 이후인지 확인하십시오.