Tại sao tràn bộ đệm không phải là vấn đề với Java hoặc Python?

Tràn bộ đệm từ lâu đã là một đặc điểm của bối cảnh bảo mật máy tính. Trên thực tế, sâu Internet tự lan truyền đầu tiên—Morris Worm của năm 1988—đã sử dụng lỗi tràn bộ đệm trong Unix finger daemon để lây lan từ máy này sang máy khác. 27 năm sau, lỗi tràn bộ đệm vẫn là nguyên nhân gây ra sự cố. Windows đã cải tiến trọng tâm bảo mật của mình một cách nổi tiếng sau hai lần khai thác theo hướng tràn bộ đệm vào đầu những năm 2000. Và chỉ trong tháng 5 này, một lỗi tràn bộ đệm được tìm thấy trong trình điều khiển Linux đã khiến (có khả năng) khiến hàng triệu bộ định tuyến dành cho gia đình và văn phòng nhỏ dễ bị tấn công

Về cốt lõi, lỗi tràn bộ đệm là một lỗi đơn giản đến kinh ngạc xuất phát từ một thực tế phổ biến. Các chương trình máy tính thường hoạt động trên các khối dữ liệu được đọc từ tệp, từ mạng hoặc thậm chí từ bàn phím. Các chương trình phân bổ các khối bộ nhớ có kích thước hữu hạn—các bộ đệm—để lưu trữ dữ liệu này khi chúng làm việc trên đó. Tràn bộ đệm xảy ra khi nhiều dữ liệu được ghi vào hoặc đọc từ bộ đệm hơn bộ đệm có thể chứa

Thoạt nhìn, đây có vẻ là một lỗi khá ngu ngốc. Xét cho cùng, chương trình biết bộ đệm lớn đến mức nào, vì vậy, thật đơn giản để đảm bảo rằng chương trình không bao giờ cố nhồi nhét vào bộ đệm nhiều hơn mức nó biết sẽ phù hợp. Bạn sẽ đúng khi nghĩ rằng. Tuy nhiên, lỗi tràn bộ đệm vẫn tiếp tục xảy ra và kết quả thường là một thảm họa bảo mật

Để hiểu tại sao lại xảy ra lỗi tràn bộ đệm—và tại sao tác động của chúng lại nghiêm trọng như vậy—chúng ta cần hiểu một chút về cách chương trình sử dụng bộ nhớ và hiểu thêm một chút về cách lập trình viên viết mã của họ. (Lưu ý rằng chúng ta sẽ chủ yếu xem xét lỗi tràn bộ đệm ngăn xếp. Đây không phải là loại sự cố tràn bộ nhớ duy nhất, mà là loại cổ điển, nổi tiếng nhất. )

xếp nó lên

Lỗi tràn bộ đệm chỉ gây ra sự cố đối với mã gốc—nghĩa là các chương trình sử dụng trực tiếp tập lệnh của bộ xử lý thay vì thông qua một số dạng trung gian như trong Java hoặc Python. Các lỗi tràn được gắn với cách bộ xử lý và các chương trình mã gốc thao túng bộ nhớ. Các hệ điều hành khác nhau có những đặc điểm riêng, nhưng mọi nền tảng được sử dụng phổ biến ngày nay về cơ bản đều tuân theo cùng một mô hình. Để hiểu cách các cuộc tấn công này hoạt động và một số điều mọi người làm để cố gắng ngăn chặn chúng, trước tiên chúng ta phải hiểu một chút về cách bộ nhớ đó được sử dụng

Khái niệm trung tâm quan trọng nhất là địa chỉ bộ nhớ. Mỗi byte bộ nhớ riêng lẻ có một địa chỉ số tương ứng. Khi bộ xử lý tải và lưu trữ dữ liệu từ bộ nhớ chính (RAM), nó sẽ sử dụng địa chỉ bộ nhớ của vị trí mà nó muốn đọc và ghi từ đó. Bộ nhớ hệ thống không chỉ được sử dụng cho dữ liệu; . Điều này có nghĩa là mọi chức năng của một chương trình đang chạy cũng có một địa chỉ

Trong những ngày đầu của máy tính, bộ xử lý và hệ điều hành đã sử dụng địa chỉ bộ nhớ vật lý. mỗi địa chỉ bộ nhớ tương ứng trực tiếp với một phần RAM cụ thể. Mặc dù một số phần của hệ điều hành hiện đại vẫn phải sử dụng các địa chỉ bộ nhớ vật lý này, nhưng tất cả các hệ điều hành ngày nay đều sử dụng sơ đồ gọi là bộ nhớ ảo.

Với bộ nhớ ảo, sự tương ứng trực tiếp giữa địa chỉ bộ nhớ và vị trí thực trong RAM bị hỏng. Thay vào đó, phần mềm và bộ xử lý hoạt động bằng địa chỉ bộ nhớ ảo. Hệ điều hành và bộ xử lý cùng nhau duy trì ánh xạ giữa địa chỉ bộ nhớ ảo và địa chỉ bộ nhớ vật lý

Ảo hóa này cho phép một loạt các tính năng quan trọng. Đầu tiên và quan trọng nhất là bộ nhớ được bảo vệ. Mỗi quy trình riêng lẻ có bộ địa chỉ riêng. Đối với quy trình 32 bit, các địa chỉ đó bắt đầu từ 0 (đối với byte đầu tiên) và chạy tới 4.294.967.295 (hoặc theo hệ thập lục phân, 0xffff'ffff; 232 - 1). Đối với quy trình 64 bit, chúng chạy hết cỡ lên tới 18.446.744.073.709.551.615 (0xffff'ffff'ffff'ffff, 264 - 1). Vì vậy, mọi quy trình đều có địa chỉ riêng của nó 0, địa chỉ riêng của nó 1, địa chỉ riêng của nó 2, v.v.

(Trong phần còn lại của bài viết này, tôi sẽ tiếp tục nói về các hệ thống 32 bit, trừ khi có ghi chú khác. Các hệ thống 32-bit và 64-bit về cơ bản hoạt động theo những cách giống nhau, vì vậy mọi thứ đều dịch đủ tốt; . )

Bởi vì mỗi tiến trình có một bộ địa chỉ riêng của nó, những sơ đồ này theo một cách rất đơn giản để ngăn chặn một tiến trình làm hỏng bộ nhớ của bất kỳ tiến trình nào khác. tất cả các địa chỉ mà một tiến trình có thể sử dụng bộ nhớ tham chiếu chỉ thuộc về tiến trình đó. Các quy trình xử lý cũng dễ dàng hơn nhiều; . Ví dụ, chúng thường không liền kề nhau; . Bộ nhớ từ thẻ PCIe cũng thường chiếm một số không gian địa chỉ này. Địa chỉ ảo không có những bất tiện này

Vì vậy, một quá trình có gì trong không gian địa chỉ của nó? . Điều không thú vị là, trong hầu hết các hệ điều hành, "nhân hệ điều hành. " Vì lý do hiệu suất, không gian địa chỉ thường được chia thành hai nửa, với nửa dưới được chương trình sử dụng và nửa trên là không gian địa chỉ của nhân. Một nửa bộ nhớ kernel không thể truy cập vào một nửa của chương trình, nhưng bản thân kernel có thể đọc bộ nhớ của chương trình. Đây là một trong những cách mà dữ liệu được chuyển đến các chức năng kernel

Quảng cáo

Điều đầu tiên mà chúng ta cần quan tâm là các tệp thực thi và các thư viện cấu thành chương trình. Tệp thực thi chính và tất cả các thư viện của nó đều được tải vào không gian địa chỉ của quy trình và tất cả các chức năng cấu thành của chúng đều có địa chỉ bộ nhớ theo đó

Thứ hai là bộ nhớ mà chương trình sử dụng để lưu trữ dữ liệu mà nó đang làm việc, thường được gọi là heap. Ví dụ: điều này có thể được sử dụng để lưu trữ tài liệu hiện đang được chỉnh sửa, trang web (và tất cả các đối tượng JavaScript, CSS, v.v.) đang được xem hoặc bản đồ cho trò chơi đang được chơi

Thứ ba và quan trọng nhất là ngăn xếp cuộc gọi, thường được gọi là ngăn xếp. Đây là khía cạnh phức tạp nhất. Mỗi luồng trong một quy trình có ngăn xếp riêng. Đó là một đoạn bộ nhớ được sử dụng để theo dõi cả chức năng mà một luồng hiện đang chạy, cũng như tất cả các chức năng tiền nhiệm—những chức năng được gọi để truy cập chức năng hiện tại. Ví dụ: nếu hàm a gọi hàm b và hàm b gọi hàm 0xffff'ffff0 thì ngăn xếp sẽ chứa thông tin về a, b0xffff'ffff3, theo thứ tự đó

Tại sao tràn bộ đệm không phải là vấn đề với Java hoặc Python?

Phóng to / Ở đây chúng ta thấy bố cục cơ bản của ngăn xếp với bộ đệm 64 ký tự có tên là 0xffff'ffff4, sau đó là con trỏ khung và sau đó là . 0xffff'ffff5 có địa chỉ của đỉnh ngăn xếp, 0xffff'ffff6 có địa chỉ của con trỏ khung.

Ngăn xếp cuộc gọi là phiên bản chuyên biệt của cấu trúc dữ liệu "ngăn xếp" tổng quát hơn. Ngăn xếp là các cấu trúc có kích thước thay đổi để lưu trữ các đối tượng. Các đối tượng mới có thể được thêm ("đẩy") vào một đầu của ngăn xếp (thường được gọi là "đỉnh" của ngăn xếp) và các đối tượng có thể bị xóa ("bật") khỏi ngăn xếp. Chỉ có thể sửa đổi phần trên cùng của ngăn xếp bằng một lần nhấn hoặc bật, do đó, ngăn xếp buộc phải sắp xếp theo trình tự. mục được đẩy gần đây nhất là mục xuất hiện đầu tiên. Mục đầu tiên được đẩy vào ngăn xếp là mục cuối cùng được bật lên

Điều quan trọng nhất mà ngăn xếp cuộc gọi thực hiện là lưu trữ địa chỉ trả lại. Hầu hết thời gian, khi một chương trình gọi một chức năng, chức năng đó sẽ làm bất cứ điều gì nó phải làm (bao gồm cả việc gọi các chức năng khác), rồi quay trở lại chức năng đã gọi nó. Để quay lại chức năng gọi, phải có một bản ghi về chức năng gọi đó là gì. việc thực thi sẽ tiếp tục từ lệnh sau lệnh gọi hàm. Địa chỉ của lệnh này được gọi là địa chỉ trả về. Ngăn xếp được sử dụng để duy trì các địa chỉ trả về này. bất cứ khi nào một hàm được gọi, địa chỉ trả về được đẩy vào ngăn xếp. Bất cứ khi nào một hàm trả về, địa chỉ trả về sẽ được bật ra khỏi ngăn xếp và bộ xử lý bắt đầu thực hiện lệnh tại địa chỉ đó

Chức năng ngăn xếp này về cơ bản quan trọng đến mức hầu hết, nếu không muốn nói là tất cả, các bộ xử lý đều có hỗ trợ tích hợp sẵn cho các khái niệm này. Xem xét bộ xử lý x86. Trong số các thanh ghi (các vị trí lưu trữ nhỏ trong bộ xử lý có thể được truy cập trực tiếp bằng các lệnh của bộ xử lý) mà x86 xác định, hai thanh ghi quan trọng nhất là 0xffff'ffff7, viết tắt của "con trỏ lệnh" và 0xffff'ffff5, viết tắt của con trỏ ngăn xếp

0xffff'ffff5 luôn chứa địa chỉ của đỉnh ngăn xếp. Mỗi khi thứ gì đó được đẩy vào ngăn xếp, giá trị trong 0xffff'ffff5 sẽ giảm đi. Mỗi khi một thứ gì đó được lấy ra khỏi ngăn xếp, giá trị của 0xffff'ffff5 được tăng lên. Điều này có nghĩa là ngăn xếp phát triển "xuống;" . Mặc dù vậy, vị trí bộ nhớ được tham chiếu bởi 0xffff'ffff5 vẫn được gọi là "đỉnh" của ngăn xếp

0xffff'ffff7 cung cấp địa chỉ của hướng dẫn hiện đang thực hiện. Bộ xử lý duy trì chính bản thân 0xffff'ffff7. Nó đọc luồng lệnh từ bộ nhớ và tăng số lượng 0xffff'ffff7 tương ứng để nó luôn có địa chỉ của lệnh. x86 có một lệnh gọi hàm, có tên là 0xffff'ffff'ffff'ffff7 và một hướng dẫn khác để trả về từ một hàm, có tên là 0xffff'ffff'ffff'ffff8

0xffff'ffff'ffff'ffff7 lấy một toán hạng; . Khi một 0xffff'ffff'ffff'ffff7 được thực thi, con trỏ ngăn xếp 0xffff'ffff5 bị giảm đi 4 byte (32 bit) và địa chỉ của lệnh theo sau 0xffff'ffff'ffff'ffff7, địa chỉ trả về, được ghi vào vị trí bộ nhớ hiện được tham chiếu bởi 0xffff'ffff5—nói cách khác, . Sau đó, 0xffff'ffff7 được đặt thành địa chỉ được chỉ định làm toán hạng thành 0xffff'ffff'ffff'ffff7 và việc thực thi tiếp tục từ địa chỉ đó

0xffff'ffff'ffff'ffff8 làm ngược lại. 0xffff'ffff'ffff'ffff8 đơn giản không nhận bất kỳ toán hạng nào. Trước tiên, bộ xử lý đọc giá trị từ địa chỉ bộ nhớ chứa trong 0xffff'ffff5, sau đó tăng 0xffff'ffff5 lên 4 byte—nó bật địa chỉ trả về từ ngăn xếp. 0xffff'ffff7 được đặt thành giá trị này và việc thực thi tiếp tục từ địa chỉ đó

Xem 0xffff'ffff'ffff'ffff7 và 0xffff'ffff'ffff'ffff8 đang hoạt động

Nếu ngăn xếp cuộc gọi chỉ chứa một chuỗi các địa chỉ trả về, thì sẽ không có nhiều phạm vi cho các vấn đề. Vấn đề thực sự cũng xảy ra với mọi thứ khác trong ngăn xếp. Ngăn xếp là nơi lưu trữ dữ liệu nhanh chóng và hiệu quả. Lưu trữ dữ liệu trên heap tương đối phức tạp; . Nhưng ngăn xếp cũng đơn giản; . Để dọn dẹp khi dữ liệu không còn cần thiết, hãy tăng con trỏ ngăn xếp

Quảng cáo

Sự thuận tiện này làm cho ngăn xếp trở thành một nơi hợp lý để lưu trữ các biến thuộc về một hàm. Hàm có bộ đệm 256 byte để đọc một số đầu vào của người dùng? . Khi kết thúc hàm, chỉ cần thêm 256 trở lại vào con trỏ ngăn xếp và bộ đệm sẽ bị loại bỏ

Tại sao tràn bộ đệm không phải là vấn đề với Java hoặc Python?

Phóng to / Khi chúng ta sử dụng chương trình đúng cách, đầu vào bàn phím được lưu trữ trong bộ đệm 0xffff'ffff4, theo sau là một byte rỗng (không). Con trỏ khung và địa chỉ trả về không thay đổi.

Có những hạn chế đối với điều này. Ngăn xếp không phải là nơi tốt để lưu trữ các đối tượng rất lớn; . Thay vào đó, những đối tượng lớn này phải được đặt trên đống. Ngăn xếp cũng không thể sử dụng được cho các đối tượng cần tồn tại lâu hơn khoảng thời gian của một lệnh gọi hàm. Bởi vì mọi phân bổ ngăn xếp đều bị hủy khi một hàm thoát, bất kỳ đối tượng nào tồn tại trên ngăn xếp chỉ có thể tồn tại khi một hàm đang chạy. Tuy nhiên, các đối tượng trên heap không có hạn chế như vậy;

Lưu trữ ngăn xếp này không chỉ được sử dụng cho các biến được đặt tên mà các lập trình viên tạo rõ ràng trong chương trình của họ; . Theo truyền thống, đây là mối quan tâm đặc biệt nghiêm trọng trên x86. Bộ xử lý x86 không có nhiều thanh ghi (chỉ có tổng cộng 8 thanh ghi số nguyên và một số trong số đó, như 0xffff'ffff7 và 15 đã có mục đích đặc biệt), và do đó, các hàm hiếm khi có thể giữ tất cả các giá trị mà chúng cần trong thanh ghi. Để giải phóng không gian trong thanh ghi mà vẫn đảm bảo rằng giá trị hiện tại của nó có thể được lấy về sau, trình biên dịch sẽ đẩy giá trị của thanh ghi lên ngăn xếp. Sau đó, giá trị có thể được bật lên sau để đưa nó trở lại vào sổ đăng ký. Trong biệt ngữ của trình biên dịch, quá trình lưu các thanh ghi để chúng có thể được sử dụng lại được gọi là tràn

Cuối cùng, ngăn xếp thường được sử dụng để truyền đối số cho hàm. Hàm gọi lần lượt đẩy từng đối số vào ngăn xếp; . Đây không phải là cách duy nhất để chuyển đối số—chẳng hạn, chúng cũng có thể được chuyển vào sổ đăng ký—nhưng đây là một trong những cách linh hoạt nhất

Tập hợp những thứ mà một hàm có trên ngăn xếp—các biến cục bộ của nó, các thanh ghi bị tràn của nó và bất kỳ đối số nào mà nó chuẩn bị chuyển sang một hàm khác—được gọi là "khung ngăn xếp. " Vì dữ liệu trong khung ngăn xếp được sử dụng rộng rãi nên sẽ rất hữu ích khi có cách tham chiếu nhanh dữ liệu đó

Con trỏ ngăn xếp có thể làm điều này, nhưng nó hơi bất tiện. con trỏ ngăn xếp luôn trỏ tới đỉnh của ngăn xếp và do đó, nó di chuyển xung quanh khi mọi thứ được đẩy và bật ra. Ví dụ, một biến có thể bắt đầu với một địa chỉ tại 16. Hai giá trị khác có thể được đẩy lên ngăn xếp, nghĩa là biến bây giờ phải được truy cập tại 17. Sau đó, một trong những giá trị đó có thể bị bật ra, vì vậy biến hiện tại là 18

Đây không phải là một khó khăn không thể vượt qua và trình biên dịch có thể dễ dàng xử lý thử thách. Tuy nhiên, nó có thể khiến việc sử dụng con trỏ ngăn xếp để truy cập vào bất kỳ thứ gì khác ngoài "đỉnh của ngăn xếp" trở nên khó xử, đặc biệt đối với trình hợp dịch được mã hóa thủ công

Để làm cho mọi thứ dễ dàng hơn, thông thường là duy trì một con trỏ thứ hai, một con trỏ luôn lưu trữ địa chỉ của phần dưới cùng (bắt đầu) của mỗi khung ngăn xếp—một giá trị được gọi là con trỏ khung—và trên x86, thậm chí còn có một thanh ghi thường được sử dụng để . Vì điều này không bao giờ thay đổi trong một hàm nhất định, điều này cung cấp một cách nhất quán để truy cập các biến của hàm. một giá trị ở mức 20 sẽ duy trì ở mức 20 cho toàn bộ hàm. Điều này không chỉ hữu ích cho con người;

Tại sao tràn bộ đệm không phải là vấn đề với Java hoặc Python?

Phóng to / Ảnh chụp màn hình này từ Visual Studio cho thấy một số điều này đang hoạt động đối với một chương trình x86 đơn giản. Trên bộ xử lý x86, thanh ghi có tên 0xffff'ffff5 chứa địa chỉ của ngăn xếp trên cùng, trong trường hợp này là 23, được đánh dấu bằng màu xanh lam (trên x86, ngăn xếp thực sự phát triển xuống dưới, hướng tới địa chỉ bộ nhớ 0, nhưng nó vẫn được gọi là đỉnh của ngăn xếp). Hàm này chỉ có một biến ngăn xếp, 0xffff'ffff4, được tô màu hồng. Đó là bộ đệm 32 byte có kích thước cố định. Bởi vì nó là biến duy nhất, nên địa chỉ của nó cũng là 23, giống như đỉnh của ngăn xếp.

x86 cũng có một thanh ghi có tên là 0xffff'ffff6, được đánh dấu màu đỏ, (thông thường) dành riêng để lưu trữ vị trí của con trỏ khung. Con trỏ khung được đặt ngay sau các biến ngăn xếp. Ngay sau con trỏ khung là địa chỉ trả về, được đánh dấu màu xanh lá cây. Địa chỉ trả về tham chiếu một đoạn mã có địa chỉ 28. Lệnh này xuất hiện ngay sau lệnh 0xffff'ffff'ffff'ffff7, làm rõ cách sử dụng địa chỉ trả về để tiếp tục thực thi từ nơi chức năng gọi đã dừng lại

Tại sao tràn bộ đệm không phải là vấn đề với Java hoặc Python?

Phóng to /

Thật không may, 0x1ff8'00000 là một chức năng thực sự ngu ngốc. Nếu chúng ta chỉ nhấn giữ A trên bàn phím thì nó sẽ không dừng khi nó đã lấp đầy bộ đệm 0xffff'ffff4. Nó sẽ tiếp tục ghi dữ liệu vào bộ nhớ, ghi đè lên con trỏ khung, địa chỉ trả về và mọi thứ và mọi thứ khác mà nó có thể

0xffff'ffff4 trong ảnh chụp màn hình ở trên là loại bộ đệm thường xuyên bị tràn. Kích thước của nó được cố định ở chính xác 64 ký tự. Trong trường hợp này, nó chứa đầy một loạt các số và nó kết thúc bằng một giá trị rỗng cuối cùng. Rõ ràng từ hình trên, nếu hơn 64 byte được ghi vào bộ đệm 0xffff'ffff4, thì các giá trị khác trên ngăn xếp sẽ bị hỏng. Nếu ghi thêm bốn byte, thì con trỏ khung sẽ bị hủy. Nếu ghi thêm tám byte, cả con trỏ khung và địa chỉ trả về đều bị ghi đè

Rõ ràng điều này sẽ dẫn đến làm hỏng dữ liệu của chương trình, nhưng vấn đề về dòng đệm còn nghiêm trọng hơn. chúng thường dẫn đến việc thực thi mã. Điều này xảy ra vì những bộ đệm bị tràn đó sẽ không chỉ ghi đè lên dữ liệu. Họ cũng có thể ghi đè lên thứ quan trọng khác được lưu giữ trên ngăn xếp—những địa chỉ trả về đó. Địa chỉ trả về kiểm soát hướng dẫn mà bộ xử lý sẽ thực thi khi hoàn thành chức năng hiện tại; . Nếu kẻ tấn công có thể kiểm soát lỗi tràn bộ đệm, thì chúng có thể kiểm soát địa chỉ trả về;

Quá trình này có thể sẽ không có một số chức năng "thỏa hiệp máy" hay và tiện lợi để kẻ tấn công chạy, nhưng điều đó không thực sự quan trọng. Bộ đệm tương tự được sử dụng để ghi đè địa chỉ trả về cũng có thể được sử dụng để giữ một đoạn mã thực thi ngắn, được gọi là shellcode, mã này sẽ tải xuống tệp thực thi độc hại hoặc mở kết nối mạng hoặc làm bất cứ điều gì kẻ tấn công tưởng tượng.

Theo truyền thống, điều này là tầm thường vì một đặc điểm có vẻ hơi ngạc nhiên. nói chung, mỗi chương trình sẽ sử dụng cùng một địa chỉ bộ nhớ mỗi khi bạn chạy nó, ngay cả khi bạn khởi động lại giữa chừng. Điều này có nghĩa là vị trí của bộ đệm trên ngăn xếp sẽ giống nhau mỗi lần và do đó, giá trị được sử dụng để ghi đè địa chỉ trả về có thể giống nhau mỗi lần. Kẻ tấn công chỉ phải tìm ra địa chỉ đó một lần và cuộc tấn công sẽ hoạt động trên bất kỳ máy tính nào chạy mã bị lỗi

Tại sao Java và Python không dễ bị tràn bộ đệm?

Ngôn ngữ Python bảo vệ bạn khỏi lỗi tràn bộ đệm khi bạn sử dụng ngôn ngữ trừu tượng được thiết kế để ngăn chặn điều đó . Sẽ luôn có cách để vượt qua những điều trừu tượng đó và tự bắn vào chân mình.

Ngôn ngữ lập trình nào không dễ bị tấn công tràn bộ đệm?

Các ngôn ngữ như PERL, Java, JavaScript và C# sử dụng cơ chế an toàn tích hợp để giảm thiểu khả năng tràn bộ đệm.

Loại ngôn ngữ lập trình nào dễ bị tràn bộ đệm?

Hợp ngữ và C/C++ là những ngôn ngữ lập trình phổ biến dễ bị tràn bộ đệm, một phần vì chúng cho phép truy cập trực tiếp vào bộ nhớ và không được gõ mạnh

Tràn bộ đệm có còn phù hợp không?

Ngày nay, tràn bộ đệm vẫn xảy ra trong các ứng dụng phần mềm và khả năng khai thác của chúng có thể phụ thuộc vào một số yếu tố khác nhau, bao gồm trình biên dịch và/hoặc các tùy chọn trình biên dịch được sử dụng, cùng với các tính năng bảo mật của .