Một Lỗi Đặc Biệt Trong Lua 5.2.2
Vài ngày trước, phiên bản Lua 5.2.2 chính thức được phát hành, tập trung sửa chữa 4 lỗi đã biết tồn tại trong Lua 5.2.1. Trong đó có một lỗi mà gần đây tôi và một bạn đồng nghiệp từng trao đổi qua email thảo luận chi tiết.
Tôi đã cập nhật Lua 5.2.2 vào nhánh chính của dự án công ty, đồng thời phải tiến hành cập nhật cuốn sách “Chiêm ngưỡng mã nguồn Lua” do tôi biên soạn. Việc đồng bộ thay đổi mã nguồn này vào tài liệu rất vất vả nhưng đầy ý nghĩa. Trong quá trình làm việc, tôi phát hiện phiên bản mới không chỉ đơn thuần sửa 4 lỗi chính thức công bố, mà còn có nhiều điều chỉnh nhỏ giúp mã nguồn Lua trở nên chuẩn mực hơn.
Bạn An Nam đang duy trì dự án UniLua (phiên bản Lua trên C#) nên tôi tiện thể gửi thông báo về các thay đổi mã nguồn lần này để hỗ trợ dự án này cập nhật đồng bộ. Trong lúc thảo luận, bạn ấy phát hiện đoạn mã hàm luaD_precall
có điểm mâu thuẫn khó hiểu. Khi cùng nhau xem xét kỹ lưỡng vị trí được chỉ ra, chúng tôi đã tìm thấy một lỗi ẩn giấu cực kỳ tinh vi.
Chính xác mà nói, đây không phải lỗi mới xuất hiện từ Lua 5.2, mà có từ thời Lua 5.1. Tuy nhiên điều kiện kích hoạt cực kỳ khó gặp. Sau khi nắm rõ bản chất đoạn mã, tôi đã viết đoạn mã Lua nhỏ sau để tái hiện lỗi:
|
|
Đoạn mã này sẽ khiến máy ảo Lua sụp đổ khi thực thi. Patch sửa lỗi của tôi như sau:
|
|
Patch này đã được gửi đến danh sách gửi thư chính thức của Lua. Nguyên nhân gốc rễ nằm ở việc: Khi thực thi hàm Lua, ngăn xếp được mở rộng dựa trên số lượng register xác định lúc biên dịch. Tuy nhiên, nếu hàm sử dụng tham số biến đổi (vararg) và người gọi không cung cấp đủ tham số cố định, sẽ kích hoạt điều kiện biên đặc biệt trong việc sao chép tham số, dẫn đến tình trạng không gian ngăn xếp dự trữ không đủ.
Bổ sung ngày 18/4: Vì đây là lỗi tràn bộ nhớ, mức độ nghiêm trọng phụ thuộc vào môi trường biên dịch. Trong trường hợp của tôi sử dụng mingw32 với tùy chọn -g bật gỡ lỗi. Để chắc chắn quan sát được lỗi, bạn có thể thêm đoạn mã sau vào luaconf.h:
|
|
Cách này sẽ kích hoạt cơ chế assert tích hợp sẵn để phát hiện lỗi.
Cập nhật ngày 19/4: Sau nhiều lần trao đổi kỹ lưỡng, Roberto trên danh sách gửi thư Lua cuối cùng đã xác nhận tính xác thực của lỗi. Mặc dù gặp khó khăn trong việc diễn đạt bằng tiếng Anh, nhưng cuối cùng vấn đề cũng được làm rõ. Tuy nhiên phương án sửa lỗi hiện tại vẫn có thể tối ưu hơn. Roberto gợi ý: “Chúng ta có thể tránh được chi phí phát sinh nhỏ này trong trường hợp thông thường (hàm không dùng vararg). Có thể thêm p->numparams tại bộ phân tích cú pháp, hoặc kiểm tra numparams bên trong adjust_varargs chỉ dành cho hàm vararg.”
Giải pháp hoàn thiện cần xem xét kỹ lưỡng để tối ưu hiệu suất, nhưng việc xác nhận và công bố lỗi này đã mở ra cuộc thảo luận sôi nổi về cơ chế quản lý ngăn xếp trong Lua. Đây là minh chứng cho sức sống mạnh mẽ của cộng đồng mã nguồn mở, nơi mà cả những lỗi ẩn giấu hàng thập kỷ cũng sẽ được cộng tác viên phát hiện và sửa chữa.