이번 주 LWN.net 위클리 에디션(10월 8일자) 주요 내용이다. Kernel Recipes 2026에서 Greg Kroah-Hartman은 LLM이 쏟아내는 보안 버그 리포트에 커널이 어떻게 대응하고 있는지를 “당황하지 말라(don’t panic)”는 메시지와 함께 풀었고, 같은 행사에서 LLM 기반 패치 리뷰 시스템 Sashiko의 구조와 성과, 게임용으로 출발해 서버까지 넓어진 sched_ext 스케줄러 LAVD의 설계가 소개됐다. Rust 쪽에서는 사용자 정의 스마트 포인터를 내장 참조만큼 유연하게 만드는 “Beyond the &” 설계와, rustc 내부 정보를 안정된 형태로 꺼내주는 Charon이 다뤄졌다. 여기에 결국 Chromium 패키지를 포기한 Gentoo, Python의 random과 secrets 두 모듈 이야기까지 정리한다.
- Coping with the onslaught of kernel security bugs — 릴리스당 CVE 수정 4배, 하루 33건. 그래도 “당황하지 말라”는 커널 보안팀
- Last rites for Gentoo’s Chromium package — 메인테이너를 가장 많이 태워 먹은 패키지, 결국 last rites 선언
- Beyond the & — 컴파일러의 “place” 개념을 노출해 스마트 포인터를 내장 참조처럼 만드는 Rust 설계
- An update on the Sashiko patch-review system — 11단계 파이프라인, 6개월간 리뷰 17만 건, 알려진 회귀의 절반을 잡는 LLM 리뷰어
- Analyzing Rust programs with Charon — “rustc가 보는 것을 보여준다”, 안정된 JSON으로 크레이트 전체를 꺼내는 도구
- Evolving the LAVD scheduler from gaming to servers — “누가 누구를 깨우는가”로 태스크 긴급도를 추정하는 sched_ext 스케줄러의 진화
- Python’s two modules for random numbers — 재현성을 위한
random, 예측 불가능성을 위한secrets - 짧은 소식: OpenSSH 10.6, 커널 7.3-rc6, LTP 9월 릴리스, x86-64용 Raspberry Pi Desktop(trixie), Rust 1.99.0, Zig 0.17, Picard 3.0
쏟아지는 커널 보안 버그에 대처하기
LLM 덕분에 보안 버그를 찾기가 쉬워졌고, 그 결과 커널을 포함한 거의 모든 자유 소프트웨어 프로젝트에 버그 리포트가 홍수처럼 밀려들고 있다는 건 이제 비밀이 아니다. Kernel Recipes 2026에서 Greg Kroah-Hartman은 커널 보안팀이 이 홍수를 어떻게 처리하고 있는지 이야기했고, 핵심 메시지는 “당황하지 말라”였다.
그는 릴리스마다 수정된 커널 CVE 수를 그린 그래프로 시작했다. 릴리스당 약 500건으로 꾸준하던 수치가 갑자기 네 배가 됐다. 얼마 전까지만 해도 주당 50건이라는 예전 속도가 믿기지 않을 정도였는데, 지금은 하루에 33건의 CVE를 고치고 있다. 원인은 물론 LLM이다. 좋은 소식은 커널 커뮤니티가 미리 해둔 준비가 효과를 봐서 도구가 이 정도 활동량까지 확장된다는 점이다. 다른 프로젝트들은 패닉 상태지만 “우리는 괜찮고, 더 빨리 갈 수도 있다”고 했다.
언론 보도와 “종말 마케팅(doom marketing)”은 무시하면 된다고 했다. 마케팅이 어떻게 작동하는지 보여주는 일화도 소개했다. Mythos LLM이 리눅스 커널 취약점 79개를 찾았다는 전화를 받고 처음엔 걱정했지만, 원본 데이터를 받아 분석한 결과는 다음과 같았다.
| 분류 | 건수 | 비고 |
|---|---|---|
| “뭔가 크래시했다” 외 정보 없음 | 24 | 판단 불가 |
| 버그 아님 | 14 | |
| 지어낸 데이터 | 3 | 처음부터 가짜 |
| 진짜 버그지만 이미 수정됨 | 15 | 메일링 리스트에서 남이 고친 버그를 다시 “발견”하는 패턴 |
| 조치 필요한 실제 버그 | 26 | 그중 6건은 중복. 합계가 79가 안 되는 건 “LLM이 산수를 잘 못해서” |
남은 20건도 다시 걸러졌다. 7건은 악성 파일시스템 이미지로만 공격 가능한데, 커널 커뮤니티는 이를 보안 문제로 보지 않는다고 오래전부터 문서화해 왔다. 2건은 네트워크 스택 중간에 악성 패킷을 주입해야 하는데 이는 root만 할 수 있다. 2건은 NOMMU(MMU 없는) 시스템에만 해당했고 그중 하나는 io_uring이었다 — NOMMU에서 io_uring을 쓰는 사람은 아마 없을 것이다. 사소한 IPv6 문제 2건, GPU를 통해 로컬 사용자가 시스템을 장악할 수 있는 1건(그 사용자는 이미 훨씬 나쁜 짓을 할 수 있다)도 있었다. 최종적으로 진짜 버그 수정은 10건이었고, 시간당 10개 남짓한 패치를 머지하는 프로젝트에서 이는 커뮤니티 작업 한 시간 분량이었다.
진짜 문제는 익스플로잇까지 걸리는 시간
정작 걱정해야 할 것은 버그 발견부터 익스플로잇 등장까지의 평균 시간(mean time to exploit)이다. 2018년에는 63일이었는데 2026년에는 7일이다. 그런데 가장 큰 문제는 사람들이 시스템을 업데이트하지 않는다는 것이다. 오래된 소프트웨어를 돌리는 건 이제 많은 이에게 허락되지 않는 사치다.
수년간의 과소 투자에 대한 청구서가 마침내 날아왔고, 우리 소프트웨어는 완벽한 보안을 염두에 두고 설계되지 않았다. 이 버그 발견 단계를 갈아내며 지나가야 한다. 다만 처음 있는 일은 아니다. 몇 년 전 퍼저(fuzzer)가 등장했을 때도 버그가 쏟아졌고, 그걸 다 고치자 퍼저발 버그는 크게 줄었다. “버그를 다 고치면 리포트는 멈춘다.” 예로 든 것이 rsync다. Andrew Tridgell이 (욕을 좀 먹어가며) 보고된 버그를 전부 처리했고, 이제 모든 코드 스캐너가 rsync를 깨끗하다고 판정한다.
모두가 그 목표를 향해 일하는 건 아니다. 테스트도 안 된 패치를 하루 수백 개씩 리스트에 쏟아붓는 사람들이 있고, 이는 무시하면 된다. 최고의 모델도 여전히 50%는 틀리므로 LLM이 만든 패치의 50%는 틀렸다고 가정해야 한다. LLM 코드에서 자주 보이는 패턴도 있다. mutex_unlock() 호출을 mutex_destroy()로 잘못 바꾸려 하고, 장황하고 쓸모없는 체인지로그를 쓰고, 주석을 과하게 단다. 커널 개발 히스토리로 학습시킨 봇은 커뮤니티가 이미 버린 나쁜 패턴까지 배워 “욕하는 나쁜 코드(bad code that cursed)”를 만들었다. 메인테이너는 이런 코드에 주저 없이 반려해야 하고, 가장 좋은 질문은 “어떻게 테스트했나요?”다. 또 이 시스템들은 새어 나간다. 업로드한 것은 다른 사람에게도 넘어간다 — 연구자들이 하루 전에 공개 보고된 버그를 계속 “발견”하는 이유다.
이런 도구의 미래는 밝지 않다고도 했다. Coverity는 수년간 품질 높고 결정론적이기까지 한 코드 스캐너를 만들었지만 돈을 벌지 못했다. 잘 동작하는 코드 품질 도구에도 돈을 안 내는데, 오류율 50%짜리 도구에 낼 리 없다는 것이다.
커널 커뮤니티의 적응
| 대상 | 변화 |
|---|---|
| 문서 | 커널 위협 모델(threat model) 문서 보강. 봇이 실제로 읽기도 하고, 아니면 제출자에게 링크하면 된다 |
| 보안 리포트 절차 | 보안 리포트에 메인테이너 참조(CC) 필수. 정보를 덮어두려는 시도는 어차피 실패한다 |
| 리포터 | 리포트와 함께 초기 수정 패치 제출 요청 (LLM에 시키면 된다) |
| 인력 | security@kernel.org 리포트를 전담할 풀타임 개발자 자금 확보 |
| 개발자·메인테이너 | 질문에 답하지 않는 리포터·제출자에게는 응답할 의무 없음. 자체 테스트는 로컬 모델로, 비공개 정보는 외부 시스템에 올리지 말 것 |
| 추천 자료 | Chris Mason의 kres, Willy Tarreau의 로컬 모델 버그 탐색 글, 관리자는 OpenSSF의 “Securing Open Source in the Age of AI” |
그는 버그 리포트 흐름이 당분간 줄지 않을 것이고 “험난한 18개월”이 되겠지만, 먼지가 가라앉고 나면 코드는 전보다 나아져 있을 것이라고 마무리했다. 댓글에서는 “패치는 그 어느 때보다 많이 머지되는데 모두가 일이 늘고 느려졌다고 답답해한다, 패닉이 맞다”는 반론과, “LLM은 정적 분석기가 원리적으로 못 찾는 ‘의도’에 관한 로직 버그를 찾는다”는 Lustre 개발자의 경험담이 맞섰다.
Gentoo, Chromium 패키지에 last rites를 선언하다
Chromium은 많은 리눅스 사용자가 고르는 브라우저지만 배포판이 패키징하기 어렵기로 유명하다. 빌드 시스템이 복잡하고, 의존성을 대거 번들하고, 릴리스가 잦다. 여기에 사용자 불만까지 겹쳐 Gentoo의 Chromium 패키지 메인테이너들이 손을 들었다.
Gentoo는 사용자가 Portage로 소스에서 직접 빌드하는 배포판이다. USE 플래그로 컴파일 옵션을 고를 수 있고, Chromium에서는 -bundled-toolchain으로 업스트림 번들 Clang 대신 시스템 Clang을 쓸 수 있다. x86-64에서는 선택이지만 다른 아키텍처에서는 필수다 — 업스트림 번들 툴체인이 x86-64용만 있기 때문이다. 다른 배포판이 “고아(orphan)”라고 부르는 것을 Gentoo는 좀 더 극적으로 “last rites(종부성사)”라고 부른다. 선언되면 ebuild가 마스킹되고, 유예 기간 안에 누군가 인수할 기회가 주어진다.
| 날짜 | 경과 |
|---|---|
| 8월 | Roman Žilka가 Chromium ebuild의 취약점 수를 지적하는 버그 제출. Sam James는 “원래도 어려웠는데 이제 Go까지 추가됐다”고 답변 |
| 9월 9일 | Žilka: 최신 stable 151.0.7922.169에 “재앙적인 602개의 알려진 취약점”이 있다며 새 ebuild 첨부 (단 -bundled-toolchain에선 안 됨) |
| 9월 11~18일 | 사용자들이 Bentoo·Fireburn 오버레이 ebuild로 빌드 시도. 153.0.8010.47이 Arch·openSUSE·Ubuntu엔 이미 있는데 “Gentoo는 아직”이라는 불만 |
| 9월 24일 | Žilka: “This is ridiculous”. 몇 분 뒤 James: “OK then” — last rites 선언 |
| 10월 24일 | last rites 유예 기간 종료 예정 |
James의 입장은 일관됐다. “Chromium은 소스 패키지로 유지하기 끔찍하다. Gentoo에서 메인테이너를 가장 많이 태워 먹는 패키지다.” 버그에 첨부된 ebuild는 고맙지만 그냥 가져다 커밋할 수 없다. 테스트 안 된 ebuild가 깨지면 또 사용자가 불평한다. Gentoo ebuild에 대한 패치가 아니라 통째 ebuild라서 diff로 변경을 역설계해야 하고, 그건 “git add && git commit && git push로 끝나는 일이 아니다”. 오버레이 ebuild는 “상당히 자동화된” 것으로 보이며 Gentoo 버전의 수정 일부를 놓치고 있었다 — “ebuild 복사는 잘 되다가 안 되는 순간이 온다.” 한편 사용자 François Valenduc는 노트북에서 Chromium 한 번 컴파일하는 데 15~16시간이 걸려 모든 USE 조합을 테스트할 수 없다고 인정했다.
“best effort 수준으로라도 남겨둘 수 없냐”는 질문에 James는 불가하다고 답했다. Matt Whitlock은 USE 플래그 대부분과 서드파티 라이브러리 언번들링을 포기하는 대신 유지 가능한 패키지를 만들자고 제안했다. 업스트림이 “선택 기능”을 끈 빌드를 테스트하지 않으니 모든 토글을 살려두는 건 “거의 극복 불가능한 작업”이라는 것이다. James는 좋은 질문이지만 최근 복잡도의 원인은 Rust·Go 의존성 추가라고 했다. Go 빌드가 네트워크에 접근하지 않게(Gentoo ebuild에서는 금지) 손봐야 했고, 구글 개발자들은 새 릴리스에서 깨지는 실험적 Rust 기능을 계속 쓰며, 업스트림의 릴리스 tarball CI도 불안정하다. 이를 고치는 패치를 보냈지만 리뷰에 한참 걸렸고 그 뒤로도 몇 번 다시 깨졌다.
또 다른 메인테이너 Matt Jolly의 “선제적 FAQ”가 현실을 요약한다. 매주 6시간 이상이 걸리는데 그건 “말도 안 되게 많은 RAM”을 단 빠른 PC에서 컴파일 시간만 센 것이다. 패치 리베이스, 수정 체리픽, 새 버그 추적까지 합치면 매주 영업일 하루치 여가를 바쳐야 한다. C·C++·Go·JavaScript·Rust를 두루 알아야 하고, “다른 데선 절대 못 쓸 구글 자체 도구들”도 익혀야 한다. 구글 툴체인을 쓰면 단순해지지만 arm64·ppc64·RISC-V를 잃는다. 그는 최소 세 명이 필요하다고 봤다. Chromium은 대략 2주마다 새 메이저 브랜치, 매주 새 버전이 나오고, 그가 2023년에 맡은 뒤로 릴리스 주기가 두 번 더 짧아졌다.
당장 Gentoo 사용자의 선택지는 다른 ebuild를 찾거나, 다른 브라우저를 쓰거나, Flathub의 Flatpak(업스트림이 아닌 서드파티 빌드, x86-64·arm64 전용)을 쓰는 것이다. 업스트림 빌드 안내 페이지의 리눅스 링크는 503 에러를 내고, 사전 빌드 바이너리는 x86-64용뿐이다. 다른 배포판도 쉽지 않다. Fedora 패키지(“구글이 당신이 쓰기를 원치 않는 WebKit(Blink) 기반 브라우저”)와 Debian 보안 저장소는 154.0.8037.92로, 10월 1일 나온 업스트림 154.0.8037.97보다 한 버전 뒤처져 있고 이 버전에서만 알려진 CVE 11개 이상이 수정됐다.
&를 넘어서: Rust 스마트 포인터를 내장 참조처럼
Rust에는 표준 라이브러리와 사용자 정의를 합쳐 여러 스마트 포인터가 있지만, 내장 참조로는 되는데 사용자 정의 스마트 포인터로는 안 되는 연산이 있다. Rust 언어팀 리드 Tyler Mandry는 RustConf 2026에서 2026년 내내 진행한 “Beyond the &” 프로젝트를 소개했다. 문제를 보여주는 예는 해시 맵에 동적 콘텐츠를 캐시하는 코드다.
struct RenderState {
cache: HashMap<String, Arc<String>>,
template: String,
}
fn render_page(state: &mut RenderState, name: &str) -> Arc<String> {
// Mutably borrow `state.cache` using the Entry API.
let entry = state.cache.entry(name.to_string());
entry.or_insert_with(|| {
// Create the entry if it doesn't exist.
Arc::new(state.template.replace("$name", name))
}).clone()
}내장 참조 &mut RenderState로는 잘 동작한다. 그런데 스레드 간 공유를 위해 뮤텍스를 씌우면 borrow checker가 거부한다.
fn render_page(state: &Mutex<RenderState>, name: &str) -> Arc<String> {
let state: MutexGuard<'_, RenderState> = state.lock().unwrap();
// The borrow checker complains that "state" is mutably borrowed here:
let entry = state.cache.entry(name.to_string());
entry.or_insert_with(|| {
// ... but then used here while it is still borrowed:
Arc::new(state.template.replace("$name", name))
}).clone()
}원래 코드는 borrow checker가 state.cache와 state.template이 서로 다른 필드라는 걸 추적할 수 있어서 통과했다. MutexGuard를 거치면 불투명한 접근만 보이니 두 접근이 겹치지 않는다는 걸 알 수 없다. 해결책은 MutexGuard를 한 번 역참조해 다시 빌리는 것이다.
let state: &mut RenderState = &mut *state.lock().unwrap();동작은 하지만 직관적이지 않고, Mandry는 이런 복잡함이 Rust의 가파른 학습 곡선에 한몫한다고 했다. 포인터 종류마다 의미가 조금씩 다르다는 점(역참조가 안전하지 않은 raw 포인터, 쓰기만 되는 MaybeUninit 등)도 설계를 어렵게 한다.
언어팀이 Nadrieril과 Benno Lossin의 작업을 바탕으로 정한 해법은 컴파일러 내부의 “place” 개념을 사용자 코드에 노출하는 것이다. place는 C의 lvalue에 해당하는, 값을 읽고 쓸 수 있는 위치다. state.cache 같은 place는 컴파일 타임의 추상 표현이고, 포인터는 그것을 런타임에 표현하는 한 방식이다. 스마트 포인터마다 “handle” 타입을 만들고(대개 같은 위치를 가리키는 unsafe 포인터의 래퍼), 컴파일러가 프로그램이 참조하는 place를 handle로 자동 생성한다. 사용자는 handle 타입에 트레이트를 구현해 borrow checker가 그 스마트 포인터를 다루는 방식을 기존 Deref/DerefMut보다 훨씬 유연하게 지정할 수 있다.
| 트레이트 | borrow checker에 알려주는 것 |
|---|---|
ReadPlace | handle에서 값을 읽는 방법 |
WritePlace | handle이 가리키는 place에 쓰는 방법, 그리고 그것이 safe 연산인지 |
ProjectPlace | 구조체 place를 필드 place로 바꾸는 방법 (MutexGuard<RenderState> → state.template의 MutexGuard<String>) |
BorrowPlace | 주어진 place를 빌리는 새 스마트 포인터를 만드는 방법 (내장 &와 같은 역할) |
VariantPlace | 소유권을 가져가며 분해하는 destructive 패턴 매칭 지원 |
NonNull 포인터 handle에 쓰기를 가르치는 예다. SAFE가 false라 이 place에 쓰는 건 unsafe 연산으로 취급되고, 잘못 구현하면 borrow checker가 틀린 판단을 하게 되므로 트레이트 구현 자체가 unsafe다.
unsafe impl<T> WritePlace for NonNullHandle<T> {
const SAFE: bool = false;
unsafe fn write_place(self, value: T) {
unsafe { self.0.write(value) }
}
}지금은 내장 동작인 &T도 같은 메커니즘으로 정의할 수 있다. 사용자에게는 참조처럼 동작하는 스마트 포인터를 쓰는 예시가 되고, 표준 라이브러리 쪽에는 직관에 어긋나는 기존 내장 동작을 문서화할 자리가 생긴다.
unsafe impl<'a, T> BorrowPlace<&'a T> for RefHandle<'a, T> {
// Tell the borrow checker that multiple references can be made to the
// same place at the same time:
const ACCESS: AccessKind = AccessKind::Shared;
// And that the borrow lasts for the lifetime 'a:
type Timing = Lifetime<'a>;
// And that creating references is a safe operation:
const SAFE: bool = true;
unsafe fn borrow(self) -> &'a T {
unsafe { &*self.ptr.as_ptr() }
}
}MutexGuard용 handle을 비슷하게 구현하면 앞의 render_page()가 에러 없이 컴파일된다. Mandry는 이를 “예제로는 작은 코드 변경이지만 의미상으로는 큰 전환”이라고 했다. 스마트 포인터 뒤의 값에 대한 패턴 매칭(지금은 역참조 후 재대여가 필요)도 BorrowPlace가 주는 정보로 안전하게 지원할 수 있다. BorrowPlace로 새 포인터를 만드는 문법은 아직 논쟁 중이다. 같은 &를 쓰면 헷갈리고 타입 추론이 어려워지며, @ 제안도 다들 썩 만족하지 않는다.
아직 프로토타입이다. Mandry는 설계 문서에 각자 가진 “이상한 의미의” 스마트 포인터 예를 추가해 달라고 요청했고, 다음 과제로 에러 메시지를 꼽았다 — 사용자는 새 트레이트 이름이 들어간 에러를 보지 않고, 내장 참조처럼 문제를 직접 설명하는 메시지를 봐야 한다. handle 타입의 모든 연산을 지원하려면 6~8개 연산을 구현해야 한다. 열거형 일반에 대한 필드 프로젝션(예: Option 안의 필드를 프로젝션해 필드값의 Option을 얻기)은 아마 어렵지만 탐색 중이라고 했다.
Sashiko 패치 리뷰 시스템의 현황
패치 리뷰는 오래전부터 커널(과 대부분의 프로젝트)의 병목이다. LLM으로 리뷰를 자동 생성하는 Sashiko는 이미 커널 개발 프로세스의 중요한 일부가 됐다. Kernel Recipes 2026에서 메인테이너 Roman Gushchin이 동작 방식과 개선 방향을 설명했다.
목표는 새 버그가 커널에 들어가는 걸 막는 것이다. LLM을 제외한 코드는 전부 오픈소스이고 Linux Foundation에 기증됐다. 메인 인스턴스는 구글 Gemini로 돌지만 여러 인기 모델과 함께 동작하며, 커널 메일링 리스트를 감시해 보이는 패치를 전부 리뷰하고 구글이 후원하는 sashiko.dev에 올린다. 범용 LLM으로 시작했지만 리뷰 품질이 부족했다. LLM은 주어진 컨텍스트에 크게 좌우되는데, 정보가 모자라도 안 되고 너무 많으면 “이상한 효과”가 난다. 메인테이너들이 판단을 맡기는 만큼 프롬프트 인젝션 방어도 중요하고, 하루 1,500개 이상의 패치를 처리할 효율도 필요하다. 코드·프롬프트·사고 과정·생성된 리뷰를 모두 공개하는 “완전한 투명성”, 탐지율과 오탐률을 함께 재는 벤치마크, 모든 기능의 opt-in이 설계 원칙이다.
| 단계 | 역할 |
|---|---|
| 1~7 | 각자 다른 관점(락, 리소스 관리, 보안 등)에서 패치를 리뷰해 “우려 사항(concern)”을 생성 |
| 8 | 중복 제거 |
| 9 | 상충 해결 |
| 10 | 검증과 심각도 보정 |
| 11 | 리포트 생성 (곧 한 단계 추가 예정) |
핵심은 발견과 검증의 분리다. 확실히 증명할 수 있는 버그만 보고하라고 하면 많은 문제를 놓친다. 일단 우려 사항을 늘어놓고 나중에 반증을 시도하는 편이 낫다.
| 지표 (첫 6개월) | 값 |
|---|---|
| 생성한 리뷰 | 17만 건 이상, 메일링 리스트 91개 |
| Git 호출 | 1,800만 회 이상 (초기 버전은 이 때문에 I/O 바운드) |
| 패치당 리뷰 시간 | 15시간 → 15~30분 |
| 벤치마크 (알려진 회귀 1,000개) | 절반을 약간 넘게 탐지 — 전부 원래 사람 리뷰를 통과했던 버그 |
| Sashiko를 인용한 커널 커밋 | 1,215개 |
| 리뷰에 대한 이메일 답장 | 6,600건, 커널 개발자 1,015명 |
| 별도 DB에 쌓인 기존 코드 버그 | 약 5,000건 |
실제로 커널 코드가 좋아졌는지는 아직 측정하기 어렵다. 찾은 버그 상당수가 다음 패치 버전에서 조용히 고쳐지기 때문이다. 적용 후 60일 안에 수정 커밋이 붙은 패치를 살펴보니 7.1에서는 그런 경우가 줄어든 것처럼 보이지만 단정하긴 이르다. 다만 Sashiko가 “깨끗하다”고 한 패치는 이후 수정이 적었고, 문제를 보고한 패치는 수정이 더 많았다.
새 기능으로는 터미널 기반 로컬 리뷰 모드가 있다. 설치한 뒤 LLM 키만 설정하면 된다.
$ cargo install sashiko패치를 리뷰하다 주변 코드의 기존 버그를 찾는 경향이 있는데, 하려던 일이 아니라며 반기지 않는 개발자가 많아 이런 리포트는 별도 DB로 돌렸다. MAINTAINERS 파일에 이름이 있는 사람은 자기 서브시스템 버그를 조회할 수 있다. 다만 이미 고친 항목을 정리할 사람이 없어 에이전트에게 맡길 계획이다. Sashiko 자신의 변경을 리뷰하는 “Sashiko for Sashiko”도 생겼고, 누군가 OpenBSD 패치용 인스턴스를 돌리고 있으며, Git·LLVM·QEMU용 인스턴스도 검토 중이다. 남은 약점은 큰 변경에 대한 리뷰 품질, 바뀌지 않은 코드에서 매번 새 버그를 찾아내는 경향, 그리고 여전한 미탐과 환각이다.
질의응답에서는 프롬프트 상당수가 커널 문서와 겹치니 아예 커널 문서로 넣자는 제안이 나왔다. Gushchin은 원칙적으로 동의하지만 Sashiko가 구버전 커널에도 쓰이고 프롬프트가 커널 릴리스 주기보다 빨리 바뀌어 밖에 두는 편이 낫다고 했다. Mark Brown은 커널 트리 밖 정보가 필요할 때(특히 아키텍처 코드) 결과가 “hot garbage”가 된다고 지적했고, Gushchin은 데이터시트 접근이 도움이 되겠지만 법적으로 항상 가능하진 않다고 답했다.
Charon으로 Rust 프로그램 분석하기
rustc 패턴 매칭 인프라 메인테이너 Nadrieril은 Kangrejos에서 Charon을 소개했다. Rust는 암묵적·자동적인 기능이 많아 코드가 하는 일을 해석하는 도구를 만들려면 컴파일러 상당 부분을 재구현하거나 rustc 내부 인터페이스를 써야 한다. 컴파일러 바이너리는 다른 프로그램이 링크할 수 있는 라이브러리의 얇은 래퍼지만, 진입점은 명시적으로 불안정하다고 문서화돼 있다. Charon의 목표는 “rustc가 보는 것을 보여주는 것”을 안정된 방식으로 제공하고, rustc 내부 변경을 따라가는 작업을 한 곳에 모으는 것이다.
charon cargo는 평소처럼 빌드하면서 크레이트 전체(타입·트레이트·함수)를 담은 JSON .llbc 파일을 추가로 만든다. 사람이 읽을 수 있게 Rust 비슷한 언어로 pretty-print도 해준다. 아무것도 안 하는 함수 하나도 drop 위치, 변수 타입, 스택 풀기(⚡) 경로까지 드러난다.
fn consume(_: String) {}// Full name: use_threads::consume
fn consume(_1: String)
{
let _0: (); // return
let _1: String; // arg #1
storage_live(_0)
_0 = ()
_0 = ()
conditional_drop[impl_Destruct_for_String::drop_glue<'0>] _1
↳⚡ unwind_continue
return
}
커널에 특히 쓸모 있는 건 bindgen이 만든 C 구조체 바인딩을 rustc가 어떻게 해석했는지 보는 일이다. list_head에 대한 출력은 rustc가 고른 레이아웃과, 그중 컴파일러 버전이 바뀌어도 보장되는 부분을 함께 보여준다.
// Full name: bindings::bindings_raw::list_head
pub struct list_head {
next: *mut list_head,
prev: *mut list_head,
}
// layout:
// size: 16usize (guaranteed:
// at_least(align_to(max(size_of::<*mut list_head>(),
// size_of::<*mut list_head>()),
// max(align_of::<*mut list_head>(),
// align_of::<*mut list_head>())))),
// align: 8usize (guaranteed:
// at_least(max(align_of::<*mut list_head>(),
// align_of::<*mut list_head>()))),
// discriminator: _,
// inhabited: true,
// variants: [
// _: {
// offset of next: 0,
// offset of prev: 8,
// inhabited: true,
// tagger: [],
// },
// ],
// repr(C),
열거형도 아닌 구조체에 variant 정보가 붙는 등 중복이 있지만, 그 덕에 JSON 구조가 극도로 규칙적이다. 구조체·열거형은 늘 같은 정보를, 함수는 클로저·메서드·최상위 함수 구분 없이 같은 형태를 가진다. std::thread::spawn() 호출을 금지하는 커스텀 린터가 60줄 정도면 된다. “rustc의 ABI를 들여다본 적 있다면 지금 울고 있을 것”이라는 게 Nadrieril의 말이다.
| 질문·제안 | 답 |
|---|---|
| rustc_public과 차이는? (Gary Guo) | 스펙트럼의 양 끝. rustc_public은 최소한의 노출·추상화, Charon은 단순하고 파싱하기 쉬운 형식과 도구까지 제공. Guo도 Charon 쪽이 낫다고 동의 |
| Coccinelle for Rust가 rust-analyzer 대신 쓰면? (Miguel Ojeda) | 잘 맞는다 |
| 최적화 패스가 코드를 어떻게 바꾸는지 볼 수 있나? (Kent Overstreet) | 아직 안 되지만 추가하기 어렵지 않을 것 |
| MIR 기준인가, elaborated MIR 기준인가? (Guo) | 기본은 MIR, --precise-drops를 주면 drop 위치가 바뀌는 최적화 이후 기준 |
| klint를 대체할 수 있나? | 완전히는 아니다. klint는 생성된 바이너리의 속성도 검사한다 |
번역 용도로도 쓰인다. Charon 자신의 OCaml 바인딩이 Charon으로 생성되고, Rust를 C나 Lean으로 옮기는 데도 쓰였다. safe Rust만 대상으로 하지만 읽기 좋은 C를 만들고, 마이크로소프트는 Rust 암호 프리미티브를 C로 옮기는 데 썼다. 다만 libcrux를 C로 변환해 BoringSSL 암호 코드를 대체하려던 패치는 성능 부족으로 반려됐다 — 읽기 좋은 C보다 빠른 C를 만드는 게 훨씬 어렵다. Charon은 개발 4년여 만에 async를 제외한 모든 Rust 기능을 지원하며 곧 1.0이 나온다.
게임에서 서버로, LAVD 스케줄러의 진화
BPF로 CPU 스케줄러를 만들 수 있게 한 extensible scheduler class(sched_ext)는 스케줄러 혁신을 폭발시켰고, LAVD는 그중 가장 주목받은 스케줄러다. Kernel Recipes 2026에서 Changwoo Min과 Gavin Guo가 LAVD의 개요와 진화 과정을 발표했다.
LAVD는 2023년 SteamOS를 겨냥해 게임 끊김(stuttering)을 잡으려고 시작됐다. CFS 튜닝을 많이 해봤지만 보편적으로 통하는 노브는 없었다. 게임에서 가장 흔한 시스템 콜이 futex()라는 관찰이 출발점이었다 — 많은 스레드가 끊임없이 서로 통신한다. 리눅스 스케줄러는 평균 성능을 최적화하지만 게임에는 꼬리 지연(tail latency)이 더 중요하다. 게임은 A가 일을 끝내고 B를 깨우고 B가 C를 깨우는 처리 체인이라, B가 늦으면 C도 늦는다. 이 “누가 누구를 깨우는가” 패턴이 LAVD 설계의 핵심이다. 지금은 개발자 42명이 900개 넘는 커밋을 쌓아 약 12,000줄이 됐다.
| 메커니즘 | 동작 |
|---|---|
| criticality | 태스크별로 wait 빈도(남을 기다리며 자는 빈도), wake 빈도(남을 깨우는 빈도), 평균 실행 시간을 추적. criticality ∝ (wait × wake) / 평균 실행 시간. 평균 실행 시간이 짧을수록 지연에 취약하다 |
| criticality 상속 | 긴급도가 자신이 기다리는 태스크와 자신을 기다리는 태스크 양쪽으로 전파 |
| 데드라인 | earliest deadline first. 다음 데드라인까지의 시간을 마지막에 criticality로 나눈다 — 10배 critical하면 데드라인이 10배 가까워진다 |
| 타임 슬라이스 | 10ms 창 안에 모든 태스크를 한 번씩 돌리는 것이 목표. 슬라이스 = 10ms × 활성 CPU 수 / 실행 중 태스크 수 |
| 선점 | 들어오는 태스크의 캐시 도메인에서 criticality가 가장 낮은 태스크를 고른다. 슬라이스가 곧 끝날 태스크와 락 보유자(futex() 감시)는 선점하지 않는다. IPI로 강제 선점하지 않고 대상의 슬라이스를 줄여 다음 스케줄링 지점에서 자연스럽게 양보하게 한다 |
| 디스패치 큐 | L3 캐시 도메인당 하나. 일이 떨어진 CPU는 다른 도메인에서 태스크를 가져온다. 부하 분산은 태스크 수가 아니라 가중치(작업량) 기준 |
| turbulent CPU | 인터럽트를 많이 처리하거나 RT 태스크를 돌리는 CPU는 데드라인 보장이 어렵다고 보고, critical 태스크는 “steady” CPU에 배치 |
| 캐시 warmness | 태스크가 CPU에서 돈 시간으로 warmness를 계산하고 떠나 있으면 감쇠. 유휴 CPU로 옮길 가치가 캐시 손실보다 큰지 판단 |
얼마나 많은 CPU를 깨워둘지도 다룬다. 태스크를 모든 CPU로 최대한 퍼뜨리면 덜 쓰이는 코어가 저전력 상태를 오가며 낮은 성능과 높은 전력 소비라는 최악의 조합이 된다. 차라리 적은 CPU를 쓰고 나머지는 재우는 게 낫다. LAVD는 시작할 때 커널 에너지 모델(CPU별·전력 단계별 처리 능력)을 분석해 부하 수준마다 쓸 CPU 표를 만들고, 실행 중에는 전체 사용률로 표만 찾는다. 지연 criticality와 CPU 사용량으로 계산한 “성능 criticality”가 높은 태스크는 큰(빠른) 코어에 우선 배치한다.
진행 중인 작업은 cgroup 통합이다. 새 라이브러리 scx_cgroup이 커스텀 스케줄러에 cgroup 지원을 제공하며, 비용을 낮추려고 “eventual enforcement” 모델을 택했다. 한 집행 주기 안에서는 어떤 그룹도 스로틀링하지 않고 장기 평균이 맞도록 하며, 비싼 사용량 계산을 스케줄러 핫패스 밖으로 뺀다. Meta가 서버 플릿에서 내부적으로 검토 중이고, 앞으로는 CPU보다 GPU 스케줄링이 중요한 AI 워크로드 지원에 관심이 있다. 핵심인 임계 경로(critical-path) 휴리스틱은 애초에 게임 전용이 아니었다. EEVDF로 아이디어를 돌려줄 계획을 묻자 Min은 아직 안정화 중이지만 결국 일부는 업스트림을 시도할 것이라고 답했다.
Python의 두 난수 모듈, random과 secrets
Python의 random과 secrets는 둘 다 난수를 주지만 비밀번호·보안 토큰에 맞는 건 하나뿐이다. 그런데도 random은 오랫동안 그런 용도로 쓰였다. Trey Hunner의 기고는 그 역사와 올바른 사용법을 정리한다.
random은 Python 2.3부터 Mersenne Twister를 쓴다. 상태는 32비트 수 624개 배열이고, 다 쓰면 이전 상태에서 다음 624개를 계산한다. 설계 목표는 속도와 재현성이다. random.seed()로 상태를 고정하면 이후 출력이 결정되므로 테스트에 유용하다. 원문 예제를 로컬(Python 3.12.3)에서 그대로 돌리면 같은 결과가 나온다.
import random
def roll_die(rolls):
return [random.randint(1, 6) for _ in range(rolls)]
random.seed(0)
print(roll_die(10))[4, 4, 1, 3, 5, 4, 4, 3, 4, 3]
바로 이 예측 가능성이 보안과 충돌한다. 시드를 아무리 안전하게 잡아도 연속된 32비트 출력 624개를 관찰하면 내부 상태를 복원해 과거와 미래 출력을 모두 계산할 수 있다. randint()·choice()처럼 출력 비트 일부만 돌려주는 고수준 함수는 더 많은 출력과 계산이 필요할 뿐 예측 불가능해지진 않는다.
| 시기 | 변화 |
|---|---|
| Python 2.2 이전부터 | 문서에 “암호 용도로는 완전히 부적합” 경고 |
| Python 2.3 | Mersenne Twister 채택 |
| Python 2.4 | 초기 시드를 시스템 시계 대신 os.urandom() 16바이트로. SystemRandom 클래스 추가 (보안 언급은 없었음) |
| Python 3.3.3 (2013년 말) | 문서가 보안 용도로 SystemRandom을 안내하기 시작 |
| Python 3.5 | Mersenne Twister 624칸 전체(2,496바이트)를 OS 난수로 시딩 |
| 2015년 9월 | Theo de Raadt의 메일에 자극받아 Guido van Rossum이 논의 시작 — “아무도 문서를 안 읽는다”. Alyssa Coghlan이 PEP 504(기본을 시스템 RNG로) 제안, van Rossum은 “MT가 os.urandom()보다 한 자릿수 이상 빠르다”며 반대 |
| Python 3.6 (2016년) | Steven D’Aprano의 PEP 506 채택, secrets 모듈 추가 (PEP 504는 철회) |
secrets는 Mersenne Twister를 쓰지 않고 모든 난수를 OS 난수 생성기에서 받는다. 대신 재현성이 없고 느리다. 저자가 Python 3.15.0rc2에서 천만 번 돌린 결과 secrets.randbelow(100)이 약 7배 느렸다.
random.randint(0, 99): 5,866,763 per second
secrets.randbelow(100): 891,087 per second
보안 난수가 필요한 코드는 한 번에 많은 난수가 필요한 경우가 드물어 대부분 무시할 만한 차이다. secrets에는 choices()·sample()·shuffle() 등이 없는데, 두 모듈의 구조를 보면 해법이 나온다. random의 최상위 함수는 숨은 random.Random 인스턴스의 바운드 메서드이고, secrets의 함수는 random.Random을 상속한 SystemRandom 인스턴스의 메서드다. 로컬에서도 확인된다.
import random, secrets
print(random.choice.__self__)
print(secrets.choice.__self__)
print(secrets.SystemRandom.__mro__)<random.Random object at 0x2307a850>
<random.SystemRandom object at 0x23109e40>
(<class 'random.SystemRandom'>, <class 'random.Random'>, <class '_random.Random'>, <class 'object'>)
따라서 SystemRandom 인스턴스를 직접 만들면 random의 API 전체를 예측 불가능한 난수로 쓸 수 있다.
import secrets
system_random = secrets.SystemRandom()
print(system_random.sample(range(100), k=5))[8, 66, 58, 70, 59]
올해 초 urwid 프로젝트 권고처럼 random을 보안 용도로 오용하는 사례는 여전하다. 사실 예측 불가능한 난수는 2.4부터 SystemRandom으로 있었고, secrets는 찾기 쉬운 API를 더한 것에 가깝다. Coghlan이 2015년에 설명했듯 “비밀에는 secrets의 RNG를, 모델링과 시뮬레이션에는 random의 RNG를”이 random.Random과 random.SystemRandom의 기술적 차이를 설명하는 것보다 훨씬 전하기 쉬운 이야기다.
짧은 소식
- OpenSSH 10.6 — AI 보조 보안 버그 리포트가 대량으로 들어오고 있다며 “사람의 분류·분석·테스트 케이스, 특히 수정 제안이 함께 오면 매우 환영한다”고 밝혔다. 버그 수정을 다음 정기 릴리스까지 모아두지 않고 더 자주 릴리스할 계획이다. 하이브리드 포스트 양자 서명 알고리즘
ssh-mldsa44-ed25519가 활성화됐고,sftp의lmkdir/mkdir에-p옵션이 생겼다. 사이드 채널 누출 완화를 위해ssh/sshd의 LZ77 딕셔너리 코더를 꺼서 Compression 옵션의 효과가 줄어든다. 두 원격 호스트 간 복사를 하는scp -R은 보안 위험으로 폐기 예정이며 향후 무시된다. - 커널 7.3-rc6 — 10월 4일 릴리스. 리누스 토발즈는 다음 주에 메인테이너 서밋과 Linux Plumbers Conference로 자신과 여러 메인테이너가 이동 중이라 rc7 통계가 달라질 수 있고, 그다음 주는 연례 가족 휴가라 다음 머지 윈도가 “좀 삐걱거릴 수 있다”고 했다. 지금까지 개발자 2,926명(첫 기여자 764명)의 머지 제외 체인지셋 18,168건이 들어갔다. rc2 이후 주당 커밋은 681 → 733 → 598 → 649 → 596건이다. 10월 3일 7.2.9, 6.18.55, 6.12.112, 6.6.158, 6.1.189, 5.15.222, 5.10.271 안정 업데이트가 나왔다.
- Linux Test Project 2026년 9월 안정 릴리스 — 5월 릴리스 이후 저자 41명의 패치 382개가 들어갔다.
- x86-64용 Raspberry Pi Desktop — Debian 13(trixie) 기반 PC용 Raspberry Pi OS가 오랜만에 나왔다. Bullseye 이후 “모두 다른 일로 너무 바빠서” 업데이트를 못 했고 매주 두세 통씩 언제 나오냐는 메일을 받았다고 한다. Debian이 PC용 32비트 지원을 끊어 이제 64비트(amd64) 기반이다. 최근 15년 안의 PC와 대부분의 인텔 맥에서 돌지만, Debian의 Apple Silicon 지원은 아직 실험적이다.
- Rust 1.99.0 —
extern "C"가변 인자 함수 지원, raw 포인터의 크기·정렬을 얻는 함수, 여러 API 안정화가 포함됐다. Rust Foundation은 RustConf 2026(몬트리올) 녹화 영상도 공개했다. - Zig 0.17 — 5개월간 기여자 206명의 커밋 925개. 빌드 시스템을 재작업하며 Build Server Protocol을 도입했고, ELF 링커를 개선해 x86_64-linux에서는 모두가 증분 컴파일(incremental compilation)을 쓸 수 있을 것으로 기대한다.
- MusicBrainz Picard 3.0 — Qt6 업그레이드, 완전히 새로운 플러그인 시스템, UI·커버 아트 처리 개선, ISRC(국제 표준 녹음 코드) 제출 지원 등이 들어갔다.
이번 주 인용문으로는 Michael Catanzaro의 한 줄이 이번 호 전체 분위기를 요약한다. “버그 바운티 프로그램을 너무 많은 취약점을 찾아서 닫는 건 그다지 유쾌한 결과가 아니다.” 개발 쪽에서는 Emmanuele Bassi가 커뮤니티를 떠나는 일에 대해 “프로젝트는 테세우스의 배이고 우리는 모두 시간이 지나며 교체되는 썩은 판자”라고 했고, Luciano Mammino는 “Docker는 파일을 어떻게 지우나”라는 질문에서 출발해 “레이어는 작은 파일시스템이 아니라 순서 있는 파일시스템 변경 집합(changeset)”이라는 모델에 도달했다. 추가·수정은 평범한 tar 엔트리이고 삭제는 whiteout인데, 그 규칙 때문에 이름이 .wh.로 시작하는 일반 파일은 OCI 이미지를 왕복하면 살아남지 못한다.
행사는 프라하 주간의 후반부다. 10월 7~9일 Open Source Summit Europe과 Embedded Linux Conference Europe, 8일 Linux Security Summit Europe, 10~11일 GStreamer Conference, 13~15일 OpenSSL Conference가 이어진다. 이후 14~17일 케이프타운 PyCon South Africa, 16~18일 로마 VideoLAN Developers’ Days, 18~20일 All Things Open, 20~23일 The Matrix Conference(말뫼)와 PostgreSQL Conference Europe(발렌시아), 10월 30일~11월 1일 베를린 Qubes OS Summit, 11월 12~13일 온라인 Ubuntu Summit 26.10이 있다. 11월 28일에는 서울에서 FOSS for All Conference 2026이 열린다. CFP 마감은 Southern California Linux Expo가 11월 1일이다.
이번 주 핵심 요약
- Kroah-Hartman의 79건 분석이 주는 교훈은 LLM 리포트를 받는 쪽의 분류 체계가 곧 방어선이라는 것이다. 커널은 “악성 파일시스템 이미지는 보안 문제가 아니다” 같은 위협 모델을 미리 문서로 못 박아 두었기에 79건을 한 시간 분량으로 줄일 수 있었다. 자기 프로젝트에 위협 모델 문서가 없다면 지금 만들 때다. 그리고 익스플로잇까지 7일이라는 숫자는 결국 “업데이트하라”로 귀결된다.
- Sashiko에서 가져갈 설계 원칙은 “발견과 검증의 분리”다. 확실한 것만 보고하게 하면 미탐이 늘고, 일단 넓게 우려 사항을 뽑은 뒤 별도 단계에서 반증하는 편이 낫다. LLM 리뷰 파이프라인을 직접 만든다면 관점별 리뷰 → 중복 제거 → 검증·심각도 보정으로 단계를 나누는 구조를 참고할 만하다. 알려진 회귀 절반 탐지라는 수치는 사람 리뷰를 대체하지는 못해도 보완으로는 충분히 의미 있다.
- Gentoo의 Chromium 사태는 “ebuild 첨부”와 “유지보수”의 차이를 선명하게 보여준다. 업스트림이 선택 기능을 끈 빌드를 테스트하지 않고, Rust·Go 의존성과 짧아지는 릴리스 주기가 겹치면 소스 기반 배포판의 유연성 자체가 비용이 된다. 자원봉사 메인테이너에게 요구만 하는 것이 어떤 결과를 낳는지도 함께 기억할 사례다.
- “Beyond the &”는 아직 프로토타입이지만,
MutexGuard뒤에서 필드 단위 대여가 안 되는 문제는 지금도 자주 마주친다. 당장은&mut *guard로 한 번 재대여하는 관용구가 해법이고, 이게 왜 필요한지(불투명한Deref가 필드 분리를 가린다) 이해해 두면 나중에 place/handle 설계가 들어왔을 때 무엇이 바뀌는지 바로 보인다. - Charon은 Rust 코드를 대상으로 커스텀 린터나 분석 도구를 만들고 싶지만 rustc 내부 API에 묶이기 싫은 경우의 답이 될 수 있다. 특히 bindgen 결과 구조체의 레이아웃 중 무엇이 “보장”되는지 보여주는 기능은 커널 Rust 바인딩 작업에 바로 쓸모 있다. 1.0이 나오면 시도해볼 만하다.
- LAVD의 아이디어 중 범용으로 재사용할 만한 것은 “평균이 아니라 꼬리 지연”과 “누가 누구를 깨우는가로 임계 경로를 추정”이다. 그리고 “CPU를 넓게 퍼뜨리는 게 항상 좋진 않다”는 관찰은 sched_ext를 쓰지 않더라도 전력·성능 튜닝에서 기억할 만하다. 서버 쪽에서 써보려면
scx_cgroup기반 cgroup 통합이 안정화되는 시점을 지켜보면 된다. - Python 난수는 규칙 하나로 정리된다. 토큰·비밀번호·세션 ID는
secrets, 시뮬레이션·테스트는random.secrets에 없는sample()·shuffle()이 필요하면secrets.SystemRandom()인스턴스를 쓰면 된다. 코드 리뷰에서 보안 맥락의import random은 바로 짚을 대상이다.