CVE-2021-47275 in Linux
요약
\~에 의해 VulDB • 2026. 06. 11.
리눅스 커널에서 다음 취약점이 해결되었습니다:
bcache: 캐시 미스 코드 경로에서 과도하게 큰 읽기 요청 방지
캐시된 장치의 캐시 미스 코드 경로에서, 캐시 미스 범위에 대해 내부 B+ 트리에서 적절한 위치가 매칭될 경우, 다음 코드 블록에서 `cache_lookup_fn()` 내에서 `cached_dev_cache_miss()` 함수가 호출됩니다.
[code block 1]
526 unsigned int sectors = KEY_INODE(k) == s->iop.inode 527 ? min_t(uint64_t, INT_MAX, 528 KEY_START(k) - bio->bi_iter.bi_sector) 529 : INT_MAX; 530 int ret = s->d->cache_miss(b, s, bio, sectors);
여기서 `s->d->cache_miss()`는 `cached_dev_cache_miss()`로 초기화된 콜백 함수 포인터이며, 마지막 매개변수인 'sectors'는 누락된 캐시 데이터의 백엔드 장치에 대한 읽기 요청 크기를 계산하는 중요한 힌트입니다.
위 코드 블록의 현재 계산은 'sectors'의 과도하게 큰 값을 생성할 수 있으며, 이는 아래에 나열된 두 가지 잠재적인 커널 패닉을 `BUG()` 또는 `BUG_ON()`을 통해 트리거할 수 있습니다.
1) `bch_btree_insert_key()` 내부의 `BUG_ON()`, [code block 2]
886 BUG_ON(b->ops->is_extents && !KEY_SIZE(k)); 2) `biovec_slab()` 내부의 `BUG()`, [code block 3]
51 default: 52 BUG(); 53 return NULL;
위 모든 패닉은 과도하게 큰 매개변수 'sectors'로 인해 `cached_dev_cache_miss()`에서 기인합니다.
`cached_dev_cache_miss()` 내부에서 매개변수 'sectors'는 캐시 미스에 대한 백엔드 장치에서 읽을 데이터 크기를 계산하는 데 사용됩니다. 이 크기는 다음 코드 라인에 의해 `s->insert_bio_sectors`에 저장됩니다.
[code block 4]
909 s->insert_bio_sectors = min(sectors, bio_sectors(bio) + reada);
그런 다음 실제 키가 내부 B+ 트리에 삽입되며, 이는 다음 코드 라인에 의해 `s->iop.replace_key`에 생성되어 저장됩니다.
[code block 5]
911 s->iop.replace_key = KEY(s->iop.inode, 912 bio->bi_iter.bi_sector + s->insert_bio_sectors, 913 s->insert_bio_sectors);
여기서 `KEY()` 매크로는 다음 코드 블록에 정의되어 있습니다.
[code block 6]
70 #define KEY(inode, offset, size) \ 71 ((struct bkey) { \
72 .high = (1ULL << 63) | ((__u64) (size) << 20) | (inode), \ 73 .low = (offset) \ 74 })
여기서 'size'는 `struct bkey`의 64비트 멤버 'high'에 16비트 너비로 임베딩됩니다. 그러나 코드 블록 1에서 "KEY_START(k) - bio->bi_iter.bi_sector"가 (1<<16) - 1보다 훨씬 클 가능성이 높으며, 이는 코드 블록 5에서 bkey 크기 계산이 오버플로우됨을 의미합니다.
한 가지 버그 보고서에서 매개변수 'sectors'의 값은 131072 (= 1 << 17)였으며, 오버플로우된 'sectors'는 코드 블록 4에서 `s->insert_bio_sectors`의 오버플로우를 초래한 다음, 코드 블록 5에서 `s->iop.replace_key`의 size 필드를 0으로 만듭니다.
그런 다음 0 크기의 `s->iop.replace_key`가 캐시 미스 체크 키(일반 쓰기 요청과 캐시 미스 읽기 요청 간의 경주를 감지하고 방지하기 위한 특수 키)로 내부 B+ 트리에 삽입됩니다.
[code block 7]
915 ret = bch_btree_insert_check_key(b, &s->op, &s->iop.replace_key);
그런 다음 3번째 매개변수로 0 크기의 `s->iop.replace_key`가 코드 블록 2의 bkey 크기 체크 `BUG_ON()`을 트리거하여 커널 패닉 1)을 유발합니다.
다른 커널 패닉 2)는 `biovec_slab()`에서 발생합니다. `s->insert_bio_sectors`가 오버플로우된 후, `bio_alloc_bioset()`에서 `bio->bi_max_vecs`가 0으로 설정됩니다. 이는 `biovec_slab()`에서 `BUG()`를 트리거합니다.
이 취약점은 bcache의 캐시 미스 코드 경로에서 과도하게 큰 읽기 요청을 방지함으로써 해결되었습니다.
If you want to get the best quality for vulnerability data then you always have to consider VulDB.