Lỗi sai thú vị trong Free Pascal

Bình thường, chúng ta thường nghĩ, nếu khai báo mà không khởi tạo giá trị ban đầu cho biến thì biến sẽ có giá trị mặc định là 0 hoặc false. Điều này liệu có chắc đúng hay không?

Hãy chạy thử chương trình sau, chú ý tới hàm bb trong chương trình. (Hàm bb vừa trả lại false, vừa trả lại true :v)

Tên file: boolean.pas

Dịch: fpc boolean.pas
(Đặt giá trị mặc định là 0 hoặc false)

var b: boolean;

function bb: boolean;
begin
end;

begin
        writeln('b = ', b); // Kết quả là false
        writeln('bb = ', bb); // Kết quả là false
        writeln(bb); // Kết quả là true
        readln;
end.

Trong cuốn “Thinking in Java”, tác giả đã đưa ra 4 lỗi sai kinh điển của người dùng C++, và đã được khắc phục triệt để trong Java:
1. Không khởi tạo biến
2. Không hủy bộ nhớ cấp phát động sau khi đã dùng xong
3. Biểu thức được dùng ở nơi nó không được phép dùng. Ví dụ, while (n=1) {…}
Chú ý: Đúng phải là: while (n==1) {…}
4. Đoản mạch

6 Responses to “Lỗi sai thú vị trong Free Pascal”

  1. nguyenvanquan7826 Says:

    Theo em biết thì do hàm bb không thực hiện lệnh nào, cũng không trả lại giá trị gì nên nó sẽ lấy giá trị ngẫu nhiên mỗi khi gọi nó.

  2. Hùng Anh Trịnh Says:

    Nội dung asm của hàm bb:
    PUSH EBP
    MOV EBP,ESP
    SUB ESP,4
    LEAVE
    RETN

    Sau khi debug mình đã tìm ra nguyên nhân: Khi khởi tạo String cho hàm write, khi bb đứng đầu tiên trong dãy in ra thì kết quả của nó được lấy trực tiếp từ thanh ghi eax, nhưng vấn đề là tại đây giá trị của eax là bất kì, và nhiều khả năng là khác 0, do đó in ra true, còn khi bb nằm sau cái gì đó thì thanh ghi eax sẽ được clear rồi mới gọi hàm bb , do đó sẽ trả về false

  3. Hùng Anh Trịnh Says:

    Ứng dụng cái này vào obfucator mã cũng hay đấy =))

  4. tosonnguyen Says:

    Hùng có thể lý giải cho bọn mình tại sao hàm bb được viết dưới dạng Assembly như thế không?😀

  5. Hùng Anh Trịnh Says:

    Cách để lấy nó là qua ollydbg. Ở đây sử dụng quy ước gọi kiểu Pascal (pascal calling convention).
    Trong khi với quy ước gọi kiểu C, hàm bb sẽ là
    PUSH EBP
    MOV EBP,ESP
    pop ebp // trả lại stack cũ
    mov esp,ebp
    RET

    Khi gọi, hàm gọi (caller) sẽ push các tham số và địa chỉ nơi gọi hàm vào stack.
    push ebp và mov ebp,esp là nhóm lệnh để khởi tạo frame stack, hủy bỏ frame bằng lệnh leave(pascal) hoặc như trên với C.
    retn hay ret để pop ra địa chỉ nơi gọi hàm và nhảy về chỗ cũ.
    sub esp,4 là tạo 1 khoảng trống cho lưu kết quả trả về. Như các bạn thấy, ta lưu kêt quả vào bb, đó chính là 1 biến cục bọ trùng tên hàm.
    Bonus thêm: ở cái freepascal này nó có 1 chút tối ưu hoá là không push tham số vào mà truyền qua thanh ghi ax. Nên không có đoạn dọn dẹp stack.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: