System Design

Vì sao 70% lỗ hổng nghiêm trọng đến từ lỗi bộ nhớ trong C/C++

Tóm tắt

Microsoft và Chromium đều thấy khoảng 70% lỗ hổng nghiêm trọng là lỗi an toàn bộ nhớ, lớp lỗi gần như chỉ có ở C/C++. Sandbox, fuzzing hay kỷ luật code giúp giảm nhưng không loại bỏ được, nên lời giải bền vững là loại lớp lỗi từ ngôn ngữ.

Heartbleed năm 2014 cho phép kẻ tấn công đọc trộm bộ nhớ của hàng loạt máy chủ dùng OpenSSL, lấy cả khoá bí mật lẫn mật khẩu người dùng. Nguyên nhân không phải thuật toán mã hoá yếu, mà là một phép đọc vượt biên bộ đệm trong code C. Đây không phải ngoại lệ. Microsoft và đội Chromium đều công bố con số gần như trùng nhau: khoảng 70% lỗ hổng nghiêm trọng của họ là lỗi an toàn bộ nhớ, một loại lỗi gần như chỉ tồn tại ở ngôn ngữ như C và C++.

Con số từ những đội lớn nhất

NguồnPhát hiện
Microsoft (MSRC, 2019)Khoảng 70% lỗ hổng được cấp CVE mỗi năm là lỗi an toàn bộ nhớ
ChromiumKhoảng 70% lỗi bảo mật mức cao là lỗi bộ nhớ, trong đó một nửa là use-after-free (phân tích 912 lỗi cao/nghiêm trọng từ 2015)
Android (Google, 2019)76% lỗ hổng là lỗi an toàn bộ nhớ
Nhà Trắng (ONCD, 2024)Chỉ ra lỗi bộ nhớ là gốc rễ chung của Morris worm, Slammer, Heartbleed, Trident, Blastpass

Các kiểu lỗi bộ nhớ phổ biến

LỗiChuyện gì xảy raHậu quả
Buffer overflowGhi vượt quá vùng nhớ được cấpGhi đè dữ liệu, chiếm quyền thực thi code
Out-of-bounds readĐọc vượt biên (như Heartbleed)Lộ khoá, mật khẩu, dữ liệu người khác
Use-after-freeDùng con trỏ tới vùng nhớ đã giải phóngKẻ tấn công đặt dữ liệu giả vào chỗ trống đó
Double freeGiải phóng một vùng nhớ hai lầnHỏng cấu trúc quản lý bộ nhớ, dễ bị khai thác
Biến chưa khởi tạoĐọc giá trị rác còn sót lạiRò rỉ thông tin, hành vi khó lường
Data raceNhiều luồng ghi cùng dữ liệu không đồng bộHỏng dữ liệu, mở đường cho các lỗi trên
char *buf = malloc(64);
free(buf);
/* ... nhiều dòng sau, ở hàm khác ... */
strcpy(buf, user_input);   /* use-after-free + không kiểm tra độ dài */

Trình biên dịch C chấp nhận đoạn code này. Nó có thể chạy "bình thường" hàng năm trời cho tới khi ai đó gửi đúng input.

Vì sao "code cẩn thận hơn" không đủ?

  • Quy mô: codebase hàng chục triệu dòng, hàng nghìn người sửa; chỉ cần một chỗ sai.
  • Lỗi trải đều: Chromium ghi nhận lỗi bộ nhớ rải đều khắp codebase, không tập trung ở vài file "nguy hiểm".
  • Biện pháp giảm nhẹ có giới hạn: sandbox, ASLR, fuzzing, sanitizer làm khai thác khó hơn nhưng không loại bỏ lớp lỗi. Chromium cho biết sandbox và site isolation đã chạm giới hạn vì mỗi tiến trình tốn tài nguyên, nhất là trên Android.
  • Kết luận của DARPA: sau hơn hai thập kỷ, cộng đồng kỹ thuật đi tới nhận định công cụ tìm lỗi thôi là không đủ; cần ngôn ngữ từ chối chương trình không an toàn ngay lúc biên dịch.

Lưu ý để không hiểu sai

  • C/C++ không "xấu": chúng vẫn là nền của hệ điều hành, trình duyệt, game, nhúng. Vấn đề là kiểu lỗi này rất khó tránh ở quy mô lớn chỉ bằng kỷ luật.
  • 30% còn lại vẫn tồn tại: lỗi logic, phân quyền, injection không biến mất khi đổi ngôn ngữ.
  • C++ hiện đại giúp nhưng không đảm bảo: smart pointer, container có kiểm tra giảm rủi ro, nhưng trình biên dịch không bắt buộc bạn dùng chúng đúng.

Áp dụng thế nào?

  • Dev: Bật AddressSanitizer và fuzzing cho code C/C++ xử lý input bên ngoài; ưu tiên container có kiểm tra biên.
  • Tech Lead: Thống kê CVE và bug bảo mật của chính sản phẩm theo loại; nếu lỗi bộ nhớ chiếm phần lớn, đó là dữ liệu để đề xuất ngôn ngữ an toàn bộ nhớ.
  • PM: Xếp các thành phần parse dữ liệu không tin cậy (file, mạng, ảnh, video) vào nhóm rủi ro cao nhất khi lập kế hoạch bảo mật.

Nguồn & tham khảo

  1. A proactive approach to more secure code — Microsoft Security Response Center
  2. Memory safety — The Chromium Projects
  3. Back to the Building Blocks: A Path Toward Secure and Measurable Software — White House ONCD
  4. The Heartbleed Bug — heartbleed.com
  5. TRACTOR: Translating All C to Rust — DARPA