1 điểm bởi GN⁺ 2024-04-28 | 1 bình luận | Chia sẻ qua WhatsApp

Tổng quan về IPC của Hubris

  • Hubris sử dụng một kernel nhỏ, không phụ thuộc vào ứng dụng, và phần lớn mã nguồn (driver, logic ứng dụng, network stack, v.v.) nằm trong các tác vụ cách ly được biên dịch riêng
  • Các tác vụ này có thể giao tiếp với nhau bằng hệ thống nhắn tin liên tác vụ (IPC)
  • IPC của Hubris gồm 3 thao tác cốt lõi được triển khai trong kernel (RECV, SEND, REPLY)
    • RECV: nhận thông điệp đến có mức ưu tiên cao nhất hoặc chặn cho đến khi có thông điệp đến
    • SEND: gửi thông điệp và quyền điều khiển tới tác vụ nhận rồi dừng tác vụ gọi. Tác vụ gọi chuyển sang trạng thái chờ cho đến khi nhận được phản hồi
    • REPLY: gửi phản hồi cho tác vụ trước đó đã dùng SEND để nó có thể tiếp tục chạy

Chế độ lỗi mới mẻ và thú vị

  • Vì IPC vượt qua ranh giới tác vụ và các tác vụ trong Hubris là những chương trình được biên dịch riêng, cần cẩn trọng khi áp dụng cho IPC những giả định mà compiler thường đưa ra với lời gọi hàm
  • Mọi tác vụ đóng vai trò máy chủ IPC trong Hubris đều phải xử lý các lỗi tiềm ẩn sau:
    • Nhận thông điệp có interface và mã thao tác không phù hợp
    • Nhận một bó byte không thể diễn giải thay vì kiểu thông điệp được mong đợi, hoặc nhận thông điệp quá ngắn hay quá dài
    • Không nhận được vùng nhớ cần thiết (chẳng hạn vùng nhớ có thể ghi)

Trong các chương trình bình thường và đúng đắn, những tình huống này không xảy ra

  • Trong các chương trình Hubris bình thường, những tình huống nêu trên không xảy ra
  • Các tác vụ được liên kết với nhau bởi cấu hình của hệ thống build nên rất khó để nhầm lẫn lẫn nhau
  • Client và server sử dụng mã Rust được sinh ra, nên có thể giả định rằng hệ thống kiểu hoạt động xuyên qua ranh giới tác vụ

Kernel không cho phép bất kỳ điều vô lý nào

  • Trong Unix, nếu vi phạm điều kiện tiên quyết của system call thì caller sẽ nhận mã lỗi trả về, nhưng trong Hubris thì tác vụ sẽ bị hủy ngay lập tức
  • Kernel Hubris truyền lỗi cho tác vụ vi phạm quy tắc của kernel thông qua khái niệm có tên là Synthetic Fault
  • Trong Hubris, khi xảy ra lỗi phần cứng hoặc lỗi tổng hợp, tác vụ sẽ bị dừng ngay lập tức và không thể phục hồi

Máy chủ cũng không cho phép bất kỳ điều vô lý nào

  • Thông qua system call thứ 13 và kỳ lạ nhất là REPLY_FAULT, máy chủ có thể truyền lỗi cho client
  • REPLY_FAULT tương tự REPLY, nhưng thay vì gửi thông điệp và đưa tác vụ về trạng thái có thể chạy, nó truyền lỗi và dừng tác vụ
  • REPLY_FAULT giúp tránh phải xử lý các lỗi IPC không cần thiết. Chương trình bình thường có thể hoạt động như thể sự cố không thể xảy ra, còn chương trình bất thường thậm chí không có cơ hội xử lý lỗi
  • REPLY_FAULT cung cấp một cách mới để định nghĩa và triển khai các lỗi đặc thù ứng dụng, chẳng hạn như quy tắc kiểm soát truy cập

Ý kiến của GN⁺

  • REPLY_FAULT là một cơ chế mạnh cho phép máy chủ gây ra panic! xuyên tiến trình ở phía client mà không cần sự hợp tác từ phía client. Nhờ đó, Hubris trở thành một hệ thống rất thù địch với các chương trình độc hại
  • Nhược điểm của REPLY_FAULT là việc fuzzing trở nên rất khó. Một tác vụ chaos engineering tạo IPC hay system call ngẫu nhiên sẽ bị reset ngay ở gần như mọi hành vi
  • Tuy nhiên, các tác vụ Hubris bình thường không tạo thông điệp IPC một cách động, nên có thể vận hành mà không hề nhận thức được sự tồn tại của REPLY_FAULT
  • Thông qua REPLY_FAULT, máy chủ có thể gây lỗi ngẫu nhiên cho client, nhưng việc đánh giá điều này vẫn chưa hoàn tất hoàn toàn
  • Cách xử lý lỗi quyết liệt của Hubris giúp phát hiện lỗi từ sớm trong quá trình phát triển, và khác với mã lỗi, lỗi không thể bị phớt lờ
  • Nếu dùng một phương pháp phổ quát để xử lý lỗi IPC thì ngay cả các chương trình bình thường cũng có thể phải chịu overhead không cần thiết. REPLY_FAULT có vẻ là một giải pháp thanh lịch giúp tránh điều đó, đồng thời vẫn phản ứng nghiêm khắc với các chương trình bất thường

1 bình luận

 
GN⁺ 2024-04-28
Ý kiến trên Hacker News

Tóm lại như sau:

  • Có lo ngại về việc liệu REPLY_FAULT có lan truyền theo chuỗi hay không, và các lỗ hổng phát sinh từ đó

    • Trong tình huống A chờ B, và B chờ C, cần xác nhận xem nếu C gửi REPLY_FAULT thì A có bị kết thúc theo hay không
    • Nếu đúng như vậy thì toàn bộ hệ thống có thể trở nên dễ tổn thương
    • Nếu SEND tạo thành cấu trúc vòng lặp thì thậm chí có thể vô tình tự kết thúc chính mình
  • REPLY_FAULT cung cấp cách định nghĩa và triển khai các lỗi đặc thù của ứng dụng như kiểm soát truy cập

    • Điều này hữu ích khi hệ thống nhỏ, gắn kết chặt chẽ và phần lớn ứng dụng do chính người thiết kế hệ thống viết
    • Tuy nhiên khi tích hợp với mã của bên thứ ba thì đáng lo ngại vì phía bên kia có thể lập tức kết thúc tiến trình bất cứ lúc nào
    • Ngoài kia có rất nhiều driver và tiến trình nền chất lượng kém được viết bởi các lập trình viên bị quản lý ép tiến độ
  • Trong các hệ thống mà một nhóm viết toàn bộ mã nguồn, việc loại bỏ các client đáng ngờ có thể giúp tăng tốc độ lặp

  • Có thể xem Hubris là một kernel cho phép server thực hiện những tác động mà client không thể tự xử lý

    • Điều này làm cho việc tái sử dụng và cấu thành mã khó hơn, nhưng lại đơn giản hóa mô hình thực thi
    • Trong các hệ thống nhúng tĩnh, đây có thể là một sự đánh đổi hợp lý
  • Hubris và Humility là những công nghệ khiến người ta muốn đào sâu, nhưng điều đó khó thực hiện vì giới hạn về thời gian và trách nhiệm

  • Có nhắc đến một RFC Cá tháng Tư về HTTP đề xuất mã trạng thái HTTP 499 mang nghĩa "bạn nên thấy xấu hổ"

    • Điều này hợp với kiểu ngữ cảnh như "cái gì cơ... nhưng mà thật ra cũng ổn đấy"
  • Trích dẫn câu nói nổi tiếng của Einstein: "Hãy làm mọi thứ đơn giản nhất có thể, nhưng đừng đơn giản quá mức", và chỉ ra rằng thiết kế của Hubris đã vi phạm vế sau

    • Không quan tâm đến những môi trường vận hành không cho phép bất kỳ sự hỗn loạn nào của thế giới thực len vào
  • Humility là một cái tên tuyệt vời cho debugger

    • Nhiều lập trình viên cho rằng mã "tốt" thì không cần debug, nên từ chối dùng debugger
  • Trên Linux, không thể trực tiếp kết thúc chương trình khác chỉ bằng socket, nhưng với quyền root thì có thể kết thúc tiến trình khác hoặc thậm chí khởi động lại máy

    • Trong container, quyền root là chuyện phổ biến nên rủi ro này vẫn tồn tại
  • Điều này phần nào mâu thuẫn với triết lý quen thuộc của các hệ thống mạng: "hãy rộng lượng khi tiếp nhận và nghiêm ngặt khi phát đi"

    • Tuy nhiên khi thay đổi API mà vẫn phải giữ tương thích với các chương trình cũ, thì buộc phải rộng lượng ở phía tiếp nhận