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à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.

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 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 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á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):
- 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 = 2cho 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 = 2cũ. - 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.
- 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_vertexmớ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 đượcscc_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.
Bài viết liên quan

Công nghệ
Tin tức An ninh mạng: Anthropic bản đồ hóa mối đe dọa AI, Lỗ hổng Comodo chưa vá
05 tháng 6, 2026

Công nghệ
Open Terminal: Ứng dụng phong cách Bloomberg giúp dân đầu tư cá nhân tiếp cận dữ liệu tài chính chuyên sâu
04 tháng 6, 2026

Công nghệ
CEO Palantir: 10% thế giới "ghét chúng tôi một cách chuyên nghiệp"
05 tháng 5, 2026
