🇰🇷 한글 설명
Linux 커널의 BPF 검증기(Verifier)에서 32비트 연산 시 상위 비트를 0으로 채우는 제로 확장(Zero Extension) 로직에 결함이 발견되었습니다. 이 결함으로 인해 특정 경로에서 레지스터의 상위 비트에 임의의 값이 주입될 수 있는 보안 취약점이 존재합니다.
💡 쉬운 설명
컴퓨터가 숫자를 계산할 때 윗부분을 0으로 깨끗하게 비워줘야 하는데, 이 과정을 건너뛰어 엉뚱한 데이터가 남을 수 있는 문제입니다. 공격자가 이 점을 이용해 시스템이 예상하지 못한 값을 강제로 집어넣을 수 있습니다.
🇺🇸 원문 (English)
In the Linux kernel, the following vulnerability has been resolved:
bpf: Infer zext_dst based on static register liveness analysis
As reported in the thread [1], the verifier's 32-bit operations zero
extension logic is broken. This logic is responsible for correct
semantics of 32-bit operations on s390 architecture.
According to BPF semantics, operation `w1 += 1` is supposed to zero
extend the upper half of the register `r1`. On s390 the JIT relies on
the verifier emitting explicit zero extension before such operations.
The verifier attempts to minimize the amount of zero extensions
inserted by tracking whether upper halves of the 64-bit registers are
ever used. Previously such tracking worked as follows:
- bpf_reg_state->subreg_def field was set by do_check_insn()
for each operation defining lower but not the upper halves
of the register.
- Whenever an operation reading the whole register was verified,
the verifier checked register's subreg_def and set
bpf_insn_aux_data->zext_dst flag as true via a call to
mark_insn_zext() function.
- After the verification was complete, a special pass
bpf_opt_subreg_zext_lo32_rnd_hi32() extended 32-bit operations
with bpf_insn_aux_data->zext_dst set as true by adding
explicit zero extension.
Note that the logic above relies on bpf_reg_state->subreg_def,
which is a property of a current verifier state.
Before the commit [2] two additional steps happened:
- The verifier tracked upper and lower register halves' liveness as
flags REG_LIVE_READ{32,64} in bpf_reg_state->live.
- The function propagate_liveness() called mark_insn_zext()
in order to transfer the knowledge about which registers have
their upper halves alive (and thus might require zero extension).
The commit [2] removed the two steps described above,
hence making possible a situation like below:
- The register's upper half is set and is used on some verification
path P1 and the register happens not to be marked as precise.
- The checkpoint C is created while processing some instruction
between register initialization and usage.
- On some other verification path P2 the register's upper half is not
initialized and that path ends hitting the checkpoint C.
- In such a case the register's initialization on path P2 would lack
zext_dst mark, making it possible for the program to inject
an arbitrary value in the register's upper half.
This commit replaces subreg_def based logic with computing zext_dst
statically, as a part of the bpf_compute_live_registers() analysis:
- The analysis now tracks usage of upper and lower halves of the
registers separately.
- If some instruction defines a 32-bit subregister, but not the whole
register, *and* the upper half of the register is alive after that
instruction, the instruction is marked as zext_dst.
There is one notable drop in precision: whenever a BPF subprogram is
called, all 64 bits of parameter registers are presumed to be used.
The assumption is that such a drop in precision would not inflict
a noticeable performance penalty.
[1] https://lore.kernel.org/bpf/CAGKGUv=sOuqQtA1Ub-5JXfA4FPosJFYKAQE4B79cK+P1erxqtg@mail.gmail.com/
[2] commit 107e16979905 ("bpf: disable and remove registers chain based liveness")
📍 영향받는 시스템
s390 아키텍처를 사용하는 Linux 커널 (특정 커밋 107e16979905 이후 버전 포함)
⚔️ 공격 벡터
BPF 프로그램의 검증 경로를 조작하여 레지스터 상위 비트에 임의의 값을 주입하는 방식
🛡️ 대응 방법
해당 취약점이 해결된 최신 Linux 커널 버전으로 업데이트하십시오.
🔧 검증 코드
출력된 커널 버전이 취약점이 수정된 최신 패치 버전인지 확인하십시오.