Tái thiết bộ thu gom rác AF_UNIX trong nhân Linux: Phân tích lỗi bảo mật CVE-2025-40214

Công nghệ10 tháng 6, 2026·5 phút đọc

Bài viết này đi sâu vào quá trình viết lại bộ thu gom rác (GC) của hệ thống con AF_UNIX trong nhân Linux, sử dụng mô hình đồ thị và thuật toán Tarjan để tối ưu hóa hiệu suất. Đồng thời, tác giả cũng tiết lộ và phân tích chi tiết một lỗ hổng bảo mật nghiêm trọng kiểu Use-After-Free (CVE-2025-40214) xuất hiện trong quá trình triển khai mới.

Tái thiết bộ thu gom rác AF_UNIX trong nhân Linux: Phân tích lỗi bảo mật CVE-2025-40214

Bộ thu gom rác (Garbage Collector - GC) của AF_UNIX là một thành phần thú vị và phức tạp nằm sâu trong nhân Linux. Nó ra đời để giải quyết vấn đề quản lý bộ nhớ khi các socket được gửi đi thông qua cơ chế SCM_RIGHTS. Trong một số trường hợp, các socket này có thể trở nên không thể tiếp cận từ không gian người dùng (user-space) nhưng vẫn được nhân duy trì, gây lãng phí tài nguyên. Đây chính là lúc bộ GC can thiệp để giải phóng chúng.

Gần đây, hệ thống con này đã được viết lại hoàn toàn dựa trên mô hình đồ thị và thuật toán các thành phần liên kết mạnh (Strongly Connected Components - SCCs). Mặc dù mục tiêu là tăng hiệu quả và giảm độ phức tạp, nhưng bản triển khai mới lại lộ ra một lỗ hổng bảo mật nghiêm trọng. Bài viết này sẽ cùng bạn tìm hiểu cơ chế hoạt động của bộ GC mới và cách khai thác lỗi CVE-2025-40214.

Mô hình đồ thị các socketMô hình đồ thị các socket

Tại sao cần bộ thu gom rác AF_UNIX?

Trong hệ thống file ảo của Linux, mỗi socket được đại diện bởi một cấu trúc struct file. Khi một socket được gửi đi qua SCM_RIGHTS, nó được coi là đang ở trạng thái "inflight" (đang bay). Bộ đếm tham chiếu (refcount) của socket sẽ tăng lên.

Vấn đề nảy sinh khi có sự trao đổi chéo file descriptor giữa các tiến trình. Ví dụ, tiến trình A gửi socket cho B và ngược lại, sau đó cả hai đều đóng socket. Lúc này, refcount giảm về 1 nhưng không ai sở hữu handle để truy cập nó nữa. Nếu không có GC, các socket này sẽ mãi mãi chiếm dụng bộ nhớ.

Bộ GC cũ hoạt động bằng cách duyệt qua đồ thị các socket inflight, đánh dấu các chu trình (cycles) và kiểm tra điều kiện file_count == inflight để quyết định xem có nên thu hồi chúng hay không.

Minh họa thuật toán TarjanMinh họa thuật toán Tarjan

Viết lại dựa trên đồ thị và thuật toán Tarjan

Bộ GC mới được thiết kế để thay thế cách tiếp cận cũ vốn yêu cầu khóa hàng đợi nhận của từng socket, gây ảnh hưởng đến hiệu suất tổng thể. Thay vào đó, phiên bản mới mô hình hóa các socket inflight dưới dạng các đỉnh (vertices) và các file descriptor được gửi đi là các cạnh (edges) có hướng của một đồ thị.

Thuật toán cốt lõi được sử dụng là Tarjan, giúp phân chia đồ thị thành các thành phần liên kết mạnh (SCC). Một SCC là một tập hợp các đỉnh mà từ bất kỳ đỉnh nào cũng có thể đi đến các đỉnh còn lại.

Điều này quan trọng vì một chu trình (cycle) là điều kiện cần để một nhóm socket có thể "ghim" (pin) lẫn nhau, khiến chúng không thể tự giải phóng. Bộ GC mới duyệt qua các SCC này để xác định những socket nào thực sự là "rác" và cần được dọn dẹp.

Lỗi hổng Use-After-Free: CVE-2025-40214

Mặc dù được thiết kế kỹ lưỡng, bộ GC mới lại mắc phải một lỗi nghiêm trọng liên quan đến việc khởi tạo bộ nhớ. Lỗi này nằm trong cấu trúc unix_vertex, đại diện cho một đỉnh trong đồ thị.

Cụ thể, trường scc_index dùng để xác định chỉ số SCC của một đỉnh không được khởi tạo khi cấp phát mới (unix_add_edge). Trong nhân Linux, việc cấp phát bộ nhớ thường tái sử dụng các khối nhớ đã được giải phóng từ slab cache (ở đây là kmalloc-96).

Sơ đồ luồng khai thácSơ đồ luồng khai thác

Cơ chế khai thác lỗi

Kẻ tấn công có thể lợi dụng tính chất này để tạo ra một tình huống "Use-After-Free" (sử dụng sau khi giải phóng):

  1. Giai đoạn 1 (Tạo chu trình): Tạo một vòng tròn gồm các socket AF_UNIX có chu trình và kích hoạt GC. Quá trình này sẽ gán scc_index = 2 cho các đỉnh trong chu trình trước khi giải phóng chúng. Các khối nhớ này được trả lại freelist nhưng vẫn giữ lại giá trị scc_index = 2 cũ.
  2. Giai đoạn 2 (Kích hoạt Fast Path): Tạo một chu trình khác để buộc GC chuyển sang chế độ "fast path" (đường dẫn nhanh), nơi các SCC được lưu trong bộ nhớ đệm và tái sử dụng mà không cần chạy lại thuật toán Tarjan.
  3. Giai đoạn 3 (Kích hoạt lỗi): Gửi một socket mới thông qua một socket chưa từng là tiền nhiệm. Hành động này cấp phát một unix_vertex mới. Do freelist vẫn chứa dữ liệu cũ từ Giai đoạn 1, đỉnh mới này sẽ vô tình nhận được scc_index = 2.

Khi đó, hàm kiểm tra unix_vertex_dead() trên đường dẫn nhanh sẽ so sánh scc_index của các đỉnh. Vì đỉnh mới vừa cấp phát có scc_index trùng khớp với một socket đang sống (cũng có index 2), bộ GC sẽ lầm tưởng rằng socket đó đã chết và giải phóng hàng đợi nhận của nó.

Kết quả là một socket đang được sử dụng bởi người dùng bị giải phóng bộ nhớ đằng dưới, dẫn đến lỗi Use-After-Free nghiêm trọng, có thể cho phép kẻ tấn công leo thang đặc quyền (privilege escalation).

Kết luận

Lỗi CVE-2025-40214 là một lời nhắc nhở đắt giá về tầm quan trọng của việc khởi tạo biến trong lập trình nhân, đặc biệt là trong các môi trường quản lý bộ nhớ thủ công như slab allocator. Bản vá lỗi đã được đưa ra bằng cách sử dụng một bộ đếm unix_vertex_max_scc_index tăng đơn điệu để đảm bảo mọi đỉnh mới đều có một định danh duy nhất, ngăn chặn việc nhầm lẫn danh tính.

Việc tái thiết bộ thu gom rác là một bước tiến lớn về hiệu suất cho nhân Linux, nhưng nó cũng cho thấy rằng càng phức tạp hóa logic, càng dễ sinh ra các lỗ hổng tinh vi khó phát hiện.

Chia sẻ:FacebookX
Nội dung tổng hợp bằng AI, mang tính tham khảo. Xem bài gốc ↗