A Higgs-bugson in the Linux Kernel

Bài tóm tắt Jane Street 2025 3. Quy mô lớn Độ khó: Khó

Jane Street · 2025 · 3. Quy mô lớn · Độ khó: Khó — bài gốc

Bối cảnh

  • Gord là hệ lưu và phân phối dữ liệu hoạt động giao dịch của cả công ty; nó hay chép file lớn qua NFSv3 có Kerberos (mặc định NFSv3 chỉ kiểm cổng < 1024 và tin client).
  • Hiếm khi một lần chép fail với -EACCES dù quyền đúng. Không có mẫu rõ, job giống hệt chạy cùng lúc không fail cùng nhau. Manh mối duy nhất: tắt Kerberos ở dev thì không bao giờ fail.

Thay đổi chính (quá trình gỡ)

  • Kiểm rpc_gssd (daemon root lấy credential Kerberos hộ kernel qua rpc_pipefs): kernel không xin credential mới, credential còn hạn, nên không phải hết hạn khoá.
  • Chạy ghi chậm qua cuối tuần, rồi chép file khoảng 200 GB giữa hai mount NFS: không tái hiện. Để tăng song song mà không tốn băng thông và đĩa, viết một filesystem FUSE bằng crate fuser sinh byte ngẫu nhiên theo hàm băm, "chứa" file khổng lồ trong RAM (khoảng 20 phút với trợ giúp của Claude; theo tác giả, không có trợ lý AI thì đã không thử).
  • Chuẩn bị sẵn script bpftrace in stack kernel khi các hàm GSS trả -13, và bắt PCAP quanh thời điểm lỗi.
  • Tái hiện được, một phần ba số máy fail cùng lúc: gss_validate trả GSS_S_BAD_SIG. NFS server test nhỏ bị quá tải, latency hàng trăm giây, nhiều retransmission.
  • Viết plugin Wireshark tính lại checksum: checksum trong gói đúng hay sai tuỳ chọn request nào trong hai request trùng XID.
  • Nguyên nhân: checksum của response là HMAC trên GSS sequence number của request, không nằm trong response. Khi retransmit với cùng XID, kernel dùng sequence number mới và ghi đè số cũ; nếu response của request cũ tới thì kiểm checksum fail, mỗi lần fail lại gây một lần retransmit nữa (gần như tự duy trì); 3 lần liên tiếp thì trả -EACCES.
  • Viết bộ tái hiện nhẹ bằng NFQUEUE: một script Python giữ gói cho tới khi người bấm y ("tường lửa người").
  • RFC 2203 đã nói client nên giữ cache các sequence number đã gửi; Linux không làm (FreeBSD có). Hai bản vá lên upstream (Linux 6.16): thêm cache đó, và không retransmit khi thấy checksum sai (coi như không nhận được gói, để timeout bình thường lo).

Vì sao

  • Bug chỉ hiện khi server chậm tới mức client retransmit, nên test tải thấp gần như không bao giờ thấy. Tác giả tái hiện được chính vì dùng một server test quá nhỏ.
  • Cache chỉ làm hiếm đi; lỗi gốc là phản ứng tự khuếch đại (sai checksum → retransmit → sai tiếp).

Bài học cho SWE

  • Lỗi "permission denied" ngẫu nhiên có thể là lỗi giao thức dưới tầng xác thực, không phải quyền.
  • Muốn tái hiện bug hiếm: tăng song song bằng công cụ rẻ (FUSE giả dữ liệu), đặt sẵn instrumentation (eBPF, PCAP) trước khi chờ.
  • Đọc lại spec: RFC có thể đã mô tả đúng trường hợp đó.
  • Đường retry phản ứng với lỗi có thể biến một lỗi hiếm thành vòng lặp tự duy trì.

Liên quan