이번 주 LWN.net 위클리 에디션 주요 내용이다. AI 스크래퍼 봇 트래픽이 LWN 사이트 자체를 덮친 최악의 공격 사례와 그 대응, io_uring 7.2에 도입되는 락 없는 MPSC 큐, LSFMM+BPF 서밋에서 나온 Cisco의 BPF 기반 실시간 취약점 방어(“shield”) 기법과 사용자 공간 에이전트 없이 동작하는 Tetragon, 그리고 여전히 못 풀고 있는 파일시스템 테스트 인프라 공유 문제까지 다룬다.
- An update on the scraper situation — residential proxy 네트워크발 스크래퍼 트래픽이 여전히 늘고 있고, LWN도 사상 최악의 공격을 겪었다
- Lockless MPSC FIFO queues for io_uring — 7.2 커널에 도입되는 락 없는 MPSC 큐로 작업 큐 성능 개선
- Shielding running kernels against exploits with BPF — Cisco가 재부팅 없이 BPF “shield”로 취약점을 즉시 막는 방법
- Sending packets directly from BPF — 사용자 공간 에이전트 없이 BPF만으로 원격 서버와 통신하려는 Tetragon의 시도
- Topics in filesystem testing — Ted Ts’o가 제기한 ext4 회귀 증가와 테스트 결과 공유 인프라 부재 문제
- 짧은 소식: shim 시큐어부트 미폐기 취약점, 커널 7.2-rc3, Debian bookworm 마지막 정기 업데이트, Rust 1.97.0, Linus의 AI 관련 발언 논쟁
웹을 집어삼키는 스크래퍼 봇, LWN이 겪은 최악의 공격
2025년 초 LWN은 “Fighting the AI scraper bot scourge” 기사에서 LLM 학습 데이터를 노린 대규모 웹 스크래핑 문제를 다뤘다. 1년이 지난 지금도 상황은 나아지지 않았고, 오히려 정체불명 행위자들의 공격 강도는 새로운 수준으로 치솟았다. 이 트래픽의 정체와 대응 방법을 편집장 Jonathan Corbet이 다시 짚었다.
공격은 압도적으로 residential proxy(주거용 프록시) 네트워크에서 온다. 수백만 개의 고유 IP 주소가 몇 시간 동안 조율된 요청을 보내는데, 각 주소는 많아야 두세 번만 사이트를 두드리고 사라진다. User-Agent 같은 값은 모두 조작돼 일반 사용자처럼 보이도록 꾸며진다. 이미지나 CSS를 요청하지 않는 등 봇을 구별할 단서는 있지만, 그걸 알아챘을 때는 이미 그 IP가 다시 쓰이지 않는다. 이 트래픽은 중앙 명령 서버의 지시를 받아 동작하는 소프트웨어가 설치된 일반 가정용 기기·모바일 기기에서 나오며, 대부분 기기 소유자 모르게 벌어진다.
운영 주체는 크게 두 부류다. 하나는 순수 범죄 조직으로, 악성코드에 감염된 시스템을 이용한다. 연초 Google이 FBI 등과 공조해 “IPIDEA”라는 봇넷을 무력화했을 때는 LWN의 스크래퍼 트래픽도 눈에 띄게 줄어 몇 달간 평화가 이어졌지만 오래가지 못했다. 최근에는 미디어 스트리밍 기기가 악성 스크래핑 소프트웨어의 주요 매개체로 지목되기도 했다. 다른 하나는 겉으로 합법을 가장하는 부류로, Bright Data 같은 회사가 대표적이다. “무료 VPN”을 미끼로 사용자 트래픽을 자사 residential-proxy 네트워크의 일부로 편입시키는 식이다. 정작 이 프록시들을 실제로 고용해 스크래핑을 시키는 주체가 누구인지는 밝혀지지 않았는데, Corbet은 프론티어 모델 기업들이 관여했다 해도 전혀 놀랍지 않을 것이라고 썼다.
최근 LWN은 지금까지 중 가장 강력한 스크래퍼 공격을 받았다. 다행히 기존에 마련해둔 방어 체계 덕에 실제 독자 대부분은 눈치채지 못할 정도로 버텨냈지만, 구체적인 방어 수단은 “군비 경쟁”이라는 이유로 공개하지 않기로 했다. 다만 방향성은 밝혔는데, Anubis 같은 작업증명(proof-of-work) 방식은 채택하지 않았다. 정당한 사용자에게 지연을 유발하는 데다, 수백만 대의 남의 기기를 동원할 수 있는 공격자에게는 작업증명이 그리 큰 장벽이 아니어서 결국 우회당할 조짐이 보이기 때문이다. 검색엔진에 별도 allowlist를 주는 방식도 이미 강력한 독점 사업자를 더 강화하는 결과를 낳는다는 이유로 피했다. 대신 사이트 전반을 공격적으로 최적화해 값비싼 연산을 공격 상황에서 최소화하는 쪽을 택했고, 익명 방문자만 이 조치의 영향을 받고 로그인 사용자는 체감하지 못한다.
io_uring의 락 없는 MPSC 큐
io_uring을 쓰는 프로세스는 한 번에 많은 연산을 동시에 진행 중인 상태를 유지하는 경우가 많다. 지금까지 io_uring은 처리해야 할 작업 항목을 커널의 표준 lockless singly-linked list(llist)로 관리했지만, 7.2 커널부터는 락 없는 다중 생산자-단일 소비자(multi-producer, single-consumer, MPSC) 큐로 교체된다. Jonathan Corbet은 이 변경이 눈에 띄는 성능 향상을 가져오면서도, 락 없는 알고리즘이 실제로 어떻게 동작하는지 보여주는 좋은 예라고 소개했다.
llist는 단일 연결 리스트라 사실상 head에서만 접근할 수 있는 스택 구조다. io_uring은 작업을 받은 순서대로 처리해야 공정성이 보장되므로, 매번 처리 전에 리스트 순서를 뒤집는 과정이 필요했다. 큐가 너무 길어 한 번에 다 처리하지 못하면, 이미 뒤집어진 나머지 항목을 다시 원래 큐에 넣을 수 없어 별도 리스트로 관리해야 하는 번거로움도 있었다. 게다가 항목 추가는 단일 head 포인터에 대한 재시도 루프가 필요해서, 경합이 심한 상황에서는 캐시 라인 튕김(cache-line bouncing)까지 겹쳐 성능이 떨어졌다.
Jens Axboe가 올린 해법은 Dmitry Vyukov가 고안한 알고리즘을 기반으로 한 lockless MPSC 큐다. 여전히 llist_node로 항목을 엮지만, 리스트 헤드 구조가 다르다.
struct mpscq {
struct llist_node *tail;
struct llist_node stub;
};stub은 리스트가 비어 있을 때만 존재하는 센티널 항목이고, 초기화 시 tail은 이 stub을 가리킨다. 생산자가 항목을 추가하는 함수는 다음과 같다.
static inline bool mpscq_push(struct mpscq *q, struct llist_node *node)
{
struct llist_node *prev;
node->next = NULL;
prev = xchg(&q->tail, node);
WRITE_ONCE(prev->next, node);
return prev == &q->stub;
}xchg()가 새 항목을 tail에 원자적으로 밀어 넣고 이전 값을 돌려주는데, 이 연산은 완전한 배리어(full barrier)로 정의돼 있어 next 대입이 먼저 이뤄진 뒤에야 tail 교체가 다른 CPU에 보일 수 있다. 여러 CPU가 동시에 추가를 시도해도 xchg()가 순서를 직렬화해주므로 락도, 재시도 루프도 필요 없다. 소비자 쪽은 별도의 head 포인터를 mpscq 구조체와 분리해서 유지하는데, 이는 생산자와 소비자가 서로 다른 캐시 라인을 쓰게 해 경합을 피하기 위해서다. 리스트가 완전히 비면 try_cmpxchg()로 tail을 다시 stub으로 되돌리는데, 이 과정에서 동시에 새 항목을 추가하려는 생산자와 경쟁이 붙을 수 있어 실패 시 소비자는 그냥 NULL을 반환하고 다음 기회에 재시도한다.
다만 독자 댓글에서 지적됐듯, 이 큐는 학술적 의미의 “lock-free”는 아니다. xchg() 직후, next 포인터를 아직 연결하기 전에 생산자가 선점(preempt)되면, 그사이 tail은 이미 새 노드를 가리키지만 소비자는 그 노드에 도달할 방법이 없어 mpscq_pop()이 계속 NULL을 반환하는 구간이 생긴다. 원본 코드의 주석에도 이 두 명령어 폭의 창(window)이 명시돼 있으며, 소비자는 이를 “큐가 비었다”고 오판해서는 안 되고 나중에 다시 시도해야 한다. io_uring은 7.2부터 이 구조를 여러 작업 큐에 적용했고, 처리량이 늘면서 커널 내부 실행 시간은 줄었으며 리스트를 뒤집거나 별도 대기 리스트를 유지할 필요도 없어져 코드도 단순해졌다.
BPF로 실행 중인 커널 방어하기 — Cisco의 “shield”와 에이전트 없는 Tetragon
2026 LSFMM+BPF 서밋에서 Cisco의 John Fastabend는 재부팅 없이 BPF로 실행 중인 커널의 취약점을 막는 작업을 소개했다. Cisco는 소형 랙 스위치부터 초고속 대형 장비까지 다양한 하드웨어를 지원하는데, 제품 라인마다 Yocto로 빌드한 커스텀 커널을 쓰는 별도 팀이 있어 동시에 유지해야 하는 커널 종류가 많다. 보안 취약점이 발견되면 원인 파악부터 빌드, 테스트, 배포, 그리고 네트워크 중단을 감수한 재부팅까지 몇 달이 걸릴 수 있다. Fastabend 팀의 목표는 이 과정을 몇 분 단위로 줄이는 것이다.
BPF 기반 모니터링·집행 도구인 Tetragon은 스위치에서 어떤 프로그램이 언제 실행됐고 어떤 네트워크 연결을 맺었는지 시계열 데이터베이스에 기록한다. 새 CVE가 나오면 이 데이터를 뒤져 실제로 악용된 적이 있는지 확인할 수 있다. 취약점이 확인되면 해당 시스템 콜의 반환값을 BPF로 강제로 오류 처리하게 만들어 공격을 원천 차단하는데, 팀은 이런 BPF 프로그램을 “shield”라 부른다. 실제 사례로 최근 나온 “copy fail” 취약점에 대해 splice() 시스템 콜이 해당 조건에서 오류를 반환하도록 만든 몇 줄짜리 프로그램을 시연했다. 커널 라이브 패치로도 같은 효과를 낼 수 있지만, Cisco처럼 동시에 유지하는 제품 라인과 안정 커널 버전이 워낙 많으면 모든 커널에 일일이 패치하는 데 막대한 개발 자원이 든다. BPF shield는 한 번만 작성하면 지원하는 모든 커널에서 자동 테스트를 거쳐 그대로 동작한다는 장점이 있다.
한계도 있다. 어떤 CVE는 관련 서브시스템에 후킹할 지점 자체가 없어서 shield를 만들기 까다롭다. Fastabend는 커널의 에러 주입 프레임워크에 쓰이는 ALLOW_ERROR_INJECTION() 매크로가 지금보다 훨씬 폭넓게 붙어야 한다고 요청했다. 정수를 반환하고 그 값이 0과 비교돼 에러로 전파되는 함수라면 원칙적으로 다 BPF로 반환값을 조작할 수 있어야 한다는 것이다. Alexei Starovoitov는 Rust 코드라면 컴파일러가 만드는 정리(cleanup) 로직이 예측 가능한 구조라 자동화가 가능할 수 있다고 봤지만, C 코드는 취약점 제보자에게 관련 LSM 훅을 함께 요청하는 수작업이 필요할 것으로 봤다. Fastabend 팀이 특히 자주 공격받는 영역은 커널 netlink 코드라고 밝혔다.
같은 서밋의 다른 세션에서는 Tetragon 자체의 약점이 다뤄졌다. Tetragon은 수집한 데이터를 사용자 공간 에이전트를 거쳐 원격 모니터링 서버로 전달하는데, 공격자가 이 에이전트를 죽여버리면 보고 체계가 끊긴다. Song Liu, Mahé Tardy, Liam Wiseheart는 이 에이전트 없이 BPF만으로 원격 서버와 통신하는 방법을 발표했다. 2025년에는 splice() 기반 동기 전송 방식을 제안했지만 커널 개발자들의 반응이 좋지 않았고, 대신 시리얼 포트로 갈 로그 메시지를 원격으로 보내는 netconsole의 기반 인프라인 netpoll을 활용해보라는 제안을 받았다. 실제로 시도해보니 잘 동작했다. bpf_netpoll_create()로 netpoll 컨텍스트를 만들고 bpf_netpoll_send_udp()로 UDP 패킷을 보내는 kfunc를 작성해 패치를 올렸고, 데모에서는 가상머신을 띄운 뒤 사용자 공간 에이전트를 강제로 죽여도 BPF가 보내는 ping이 계속 도착하는 모습을 보여줬다.
다만 netpoll은 일반 네트워킹 스택을 우회하기 때문에 트래픽이 과도하면 다른 프로세스의 대역폭을 뺏을 수 있다는 우려(“UDP is evil”)가 나왔다. 발표 10시간 전에는 TCP로 보내는 버전도 실험했는데, 네트워킹 진영은 더 반겼지만 atomic 컨텍스트에서는 kfunc를 쓸 수 없게 되는 문제가 있었다. netpoll을 지원하는 NIC 드라이버가 전체의 10% 정도에 불과하다는 지적도 나와, 세션은 결론 없이 시간 종료로 끝났다. 이후 Tardy 팀은 7월 6일 netpoll 대신 BPF 프로그램이 UDP 커널 소켓을 직접 만들어 쓸 수 있게 하는 새 패치 세트를 올리며 작업을 이어가고 있다.
LSFMM+BPF 서밋: 파일시스템 테스트, 여전히 못 푼 숙제
파일시스템 테스트는 LSFMM+BPF 서밋에서 해마다 빠지지 않는 단골 주제다. 이번에는 Ted Ts’o가 최근 안정 커널에서 ext4 회귀가 늘고 있다는 문제를 들고 나왔다. 원인 중 하나는 folio 지원 같은 신규 기능 패치가 자동화 도구가 잡아내지 못하는 미묘한 의존성을 갖고 있기 때문이고, 다른 하나는 (LLM의 도움을 받아) 예전보다 훨씬 자주 오래된 커널로 패치가 백포트되면서 6.1·6.6 안정 커널에서 fstests 스위트를 크래시시키는 버그가 십수 건 섞여 들어온 사례다. Ts’o는 안정 커널로 향하는 패치를 감시해 fstests를 자동 실행하는 테스트 러너를 이미 구축했지만, 결과를 베이스라인과 비교해 회귀를 잡아내는 자동화는 아직 못했고, 이를 도와줄 Python 개발자를 구하고 있다고 밝혔다.
더 근본적인 논의는 테스트 결과를 커뮤니티가 공유할 중앙 저장소가 필요하다는 것이었다. kdevops 프로젝트가 fstests 실행 결과 아카이브를 갖고 있고, 커널 네트워킹 서브시스템(netdev)은 patchwork에 CI 결과를 저장하는 식으로 이미 비슷한 걸 하고 있다는 사례가 언급됐다. Ts’o는 1년 전쯤 lore.kernel.org에 테스트 결과용 메일링 리스트를 만들어달라고 요청했지만, 데이터 양을 우려한 관리자의 반응이 시큰둥했다고 전했다. Chuck Lever는 이런 중앙 데이터베이스·대시보드 구축이 서밋마다 반복해서 나오는 “문샷(moonshot)” 같은 주제라며, KernelCI 프로젝트가 이미 이 문제를 푼다고 여겨지지만 실제로는 파일시스템 테스트에 잘 맞지 않는다고 지적했다. Christian Brauner가 제기한 “가끔만 실패하는” flaky 테스트 문제에 대해서는, Ts’o가 내부적으로는 실패 시 3번 더 재시도해 전부 실패해야 보고하는 기능을 갖고 있지만 아직 공개 버전에 반영하지 못했다고 답했다. 논의는 뚜렷한 결론 없이, “다들 모여서 따로 얘기해보자”는 제안으로 마무리됐다.
짧은 소식
- shim 시큐어부트 미폐기 취약점: CMU CERT/CC 권고에 따르면, UEFI 시큐어부트 부팅에 쓰이는 shim 바이너리의 취약한 과거 버전 다수가 폐기(revocation) 목록에 아직 오르지 않았다. 관리자 권한이나 부팅 과정 변조 권한을 가진 공격자가 이를 이용해 시큐어부트를 우회하고 재부팅·재설치 후에도 살아남는 지속적 침해를 일으킬 수 있다.
- seunshare 로컬 DoS 취약점 2건: SUSE 보안팀이 SELinux 프로그램 제한용 setuid-root 바이너리 seunshare 3.10을 분석해 로컬 DoS 벡터 2건을 발견했다. targeted SELinux 정책에서는 대화형 사용자가 unconfined 컨텍스트로 동작해 사실상 root급 권한 상승으로 이어질 수 있었으며, 3.11에서 수정됐다.
- 커널 7.2-rc3 (7월 12일): 14,229개의 non-merge changeset, 2,261명의 개발자(448명이 첫 기여)가 참여했다. Linus Torvalds는 “새로운 정상 범위로 보이며, 커밋률이 다소 높지만 여름휴가 시즌 진입으로 상쇄되는 느낌”이라고 평했다.
- 2026 Maintainers Summit 주제 공모: 10월 8일 프라하에서 열리는 초청제 커널 개발 프로세스 논의 자리로, 주제 제출 마감은 7월 24일이다.
- Linus Torvalds의 AI 관련 발언 논쟁: “Linux는 반(反)AI 프로젝트가 아니며, 마음에 안 들면 포크하거나 떠나면 된다. AI는 다른 도구와 마찬가지로 유용한 도구일 뿐”이라는 발언에, Lorenzo Stoakes는 “AI는 리뷰어와 메인테이너를 무너뜨리고 있다. ‘그냥 도구’라니, 핵폭탄도 도구이긴 하다”며 반박했다.
- Debian bookworm 마지막 정기 업데이트: Debian 12(bookworm)의 마지막 일반 업데이트가 나왔다. 이후로는 보안 패치 위주의 LTS만 2028년까지 이어진다. trixie(13)도 같은 주말 다수의 동일 보안 패치가 반영된 업데이트를 받았다.
- Rust 1.97.0 릴리스: 새 심볼 맹글링(symbol-mangling) 스킴을 기본값으로 사용하고, Cargo에서 warning을 error로 처리하는 옵션을 지원하며, 빌드 성공 시 링커 출력을 숨기던 관행을 없앴다.
- Kitty / QBE 1.3 릴리스 소식: GPU 기반 터미널 에뮬레이터 Kitty는 최근 마우스 지원을 대폭 강화한 릴리스를 냈고, LLVM/GCC의 경량 대안인 컴파일러 백엔드 QBE는 1.3에서 CoreMark 기준 성능을 gcc -O2 대비 40%에서 목표치인 70%에 더 가깝게 끌어올렸다.
이번 주 핵심 요약
- AI 스크래퍼 봇 트래픽은 1년 전보다 오히려 심해졌고, LWN조차 사상 최악의 공격을 겪었다. 뚜렷한 근절책은 없고 각 사이트가 자체 최적화로 버티는 것이 현실적인 대응이다.
- io_uring의 락 없는 MPSC 큐 전환은 llist의 구조적 한계(순서 뒤집기, 재시도 루프)를 없애 성능과 코드 단순성을 동시에 얻은 사례지만, 학술적 의미의 완전한 lock-free는 아니라는 점도 짚어볼 만하다.
- Cisco의 BPF “shield”는 재부팅 없는 취약점 즉시 방어를 현실화하고 있지만,
ALLOW_ERROR_INJECTION()커버리지 확대 없이는 한계가 뚜렷하다. - Tetragon의 에이전트리스(agentless) 통신 시도는 netpoll을 활용해 일단 동작은 하지만, UDP 대역폭 남용과 드라이버 지원 부족 문제로 여전히 진행형이다.
- 파일시스템 테스트 결과 공유 인프라는 매년 서밋에서 논의되지만 뚜렷한 진전이 없는 “문샷”으로 남아 있고, LLM발 백포트 증가로 그 필요성은 더 커지고 있다.