2010-07-20 40 views
20

Vì vậy, ở đây tôi tin rằng tôi có một vấn đề tràn bộ đệm nhỏ mà tôi đã tìm thấy khi xem xét mã của người khác. Nó ngay lập tức đánh tôi như là không chính xác, và có khả năng nguy hiểm, nhưng phải thừa nhận rằng tôi không thể giải thích những hậu quả thực tế của "sai lầm" này, nếu có.Hậu quả của lỗi tràn bộ đệm này?

Tôi đã viết một ứng dụng thử nghiệm để chứng minh lỗi, nhưng tìm thấy (để mất tinh thần của tôi) rằng nó có vẻ chạy đúng bất kể tràn. Tôi muốn tin rằng đây chỉ là tình cờ, nhưng muốn có một số phản hồi để xác định xem suy nghĩ của tôi có sai hay không, nếu thực sự có vấn đề ở đây thì không thể hiện đầu trong ứng dụng thử nghiệm của tôi.

Mã vấn đề (Tôi nghĩ rằng đó là, dù sao):

char* buffer = new char[strlen("This string is 27 char long" + 1)]; 
sprintf(buffer, "This string is 27 char long"); 

Bây giờ, lý do này đứng ra cho tôi và tôi muốn gắn cờ nó như là một lỗi tràn bộ đệm có thể là vì sự strlen đầu tiên. Do số học con trỏ, vị trí 'không chính xác' của + 1 sẽ làm cho strlen trả lại 26 thay vì 27 (lấy độ dài "chuỗi của anh ấy dài 27 char"). sprintf, tôi tin rằng, sau đó in 27 char vào bộ đệm và đã gây ra một tràn bộ đệm.

Đó có phải là đánh giá chính xác không?

Tôi đã viết một ứng dụng thử nghiệm để chứng minh điều này cho người mà tôi đang xem mã và thấy rằng ngay cả trong trình gỡ lỗi chuỗi sẽ in chính xác. Tôi cũng cố gắng đặt các biến khác trên ngăn xếp và đống trước và sau mã này để xem liệu tôi có thể ảnh hưởng đến các vùng lân cận của bộ nhớ, nhưng vẫn nhận được kết quả chính xác. Tôi nhận ra rằng bộ nhớ heap mới được phân bổ của tôi có thể không liền kề, điều này sẽ giải thích sự thiếu tràn hữu ích, nhưng tôi thực sự muốn xác nhận với ý kiến ​​của người khác nếu đây thực sự là một vấn đề.

Vì đây là một "câu hỏi" khá đơn giản, nó sẽ rất tuyệt nếu bạn có thể hỗ trợ câu trả lời của bạn với một số loại tham chiếu. Trong khi tôi đánh giá cao và chào đón đầu vào của bạn, tôi sẽ không chấp nhận "có nó là" như là câu trả lời cuối cùng. Cảm ơn bạn trước.




Cập nhật: Nhiều câu trả lời tốt với rất nhiều cái nhìn sâu sắc thêm. Thật không may, tôi không thể chấp nhận tất cả. Cảm ơn bạn đã chia sẻ kiến ​​thức của mình và là 'ý kiến ​​thứ hai' của tôi. Tôi đánh giá cao sự giúp đỡ.

+4

Bạn có thể không bị cắn mã ở trên do đệm/căn chỉnh. Bạn có thể lặp lại các thử nghiệm của mình bằng chuỗi dài 64 ký tự, vì vậy phân bổ sẽ cần phải có 65 ký tự không? Và phân bổ hai chuỗi như vậy trước 'sprintf', điền chúng theo các thứ tự khác nhau. –

+4

Đó là mã khá khó chịu để lấy chuỗi thô và thêm +1 vào đó! Tôi sẽ đánh rơi việc xem xét mã chỉ trên thực tế đó một mình. –

+1

Và đây là lý do tại sao chúng tôi phát triển sử dụng nhiều thư viện được kiểm tra tốt nhất có thể ... bởi vì chúng tôi đã phạm sai lầm ngớ ngẩn như thế này! :-) @Johnson Tôi khá chắc chắn rằng các nhà phát triển có nghĩa là để thêm 1 đến chiều dài, không phải là chuỗi chính nó, do đó các lỗi. – corsiKa

Trả lời

14

Đánh giá của bạn là chính xác. [sửa] với việc bổ sung sửa chữa được đề cập bởi James Curran. [/ Edit]

Có thể, ứng dụng thử nghiệm của bạn không hiển thị sự cố vì phân bổ được làm tròn lên bội số tiếp theo là 4, 8 hoặc 16 (đó là các chi tiết phân bổ chung).

Điều này có nghĩa là bạn sẽ có thể chứng minh bằng chuỗi dài 31 ký tự.

Cách khác, sử dụng trình biên tập bộ nhớ riêng "có dụng cụ" có thể đặt các byte bảo vệ chặt chẽ xung quanh phân bổ như vậy.

6

Bạn đánh giá là chính xác, ngoại trừ việc springf sẽ đưa 28 ký tự trong bộ đệm đếm end-of-chuỗi NUL vào cuối (Đó là lý do tại sao bạn cần sự thất lạc "+1" ở nơi đầu tiên)

Lưu ý rằng trong kinh nghiệm của tôi, nếu có thứ gì đó thất bại bên ngoài trình gỡ lỗi, nhưng làm việc với bước qua trong trình gỡ lỗi, trong 100% thời gian, bạn đã tràn ngập bộ đệm cục bộ. Những kẻ lừa đảo đẩy nhiều hơn vào ngăn xếp, vì vậy ít có khả năng thứ gì đó quan trọng đã bị ghi đè.

+0

Cảm ơn bạn đã sửa lỗi trên '\ 0', tôi không chắc tại sao nó trượt tâm trí của tôi :) – KevenK

1

Có, bạn đã đúng. Bộ đệm được cấp phát sẽ là 2 byte quá nhỏ để giữ chuỗi.

Vì điều này đang được cấp phát trên heap, điều này có thể dẫn đến hỏng heap. Tuy nhiên, khả năng của nó phụ thuộc vào những phân bổ và phát hành bộ nhớ khác đã xảy ra trước thời điểm này và cũng trên trình quản lý heap đang được sử dụng. Xem Heap Overflow để biết thêm.

3

Vấn đề là bạn đang viết đâu đó trong bộ nhớ, nhưng không phải trên ngăn xếp. Do đó, thật khó để thực sự thấy có gì sai. Nếu bạn muốn nhìn thấy những thiệt hại, cố gắng bố trí chuỗi trên stack

char buffer[strlen("This string is 27 char long" + 1)]; 

và ghi qua nó. Các biến khác sẽ được viết, bạn cũng có thể thêm một số mã được thực thi nếu bạn thực sự biết cách thức hoạt động của nhị phân.

Để khai thác tràn bộ đệm như vậy, bạn cần phải ghi dữ liệu bạn muốn, sau đó tìm cách "nhảy" tới dữ liệu này sẽ được thực hiện.

+0

Điều này vẫn không * được bảo đảm * để gây ra sự cố khi phân bổ stack cũng thường được căn chỉnh với 4 hoặc 8 byte . – Roddy

1

Bạn đúng là số học con trỏ trong ví dụ này sẽ tạo ra độ dài không chính xác (ngắn hơn) được chuyển đến mới. Lý do có thể xảy ra nhất khiến bạn không thể thực hiện vụ tai nạn này là do có một số sự không chắc chắn về dung lượng bộ đệm thực sự được cung cấp bởi cấp phát bộ nhớ.

Thư viện được phép cung cấp bộ đệm lớn hơn yêu cầu. Hơn nữa, nó cũng có thể là bất cứ điều gì sau bộ đệm của bạn được tiền tố bởi một tiêu đề phân bổ đó là tùy thuộc vào quy tắc căn chỉnh từ máy. Điều này có nghĩa là có thể có tối đa ba byte đệm (tùy thuộc vào nền tảng) trước tiêu đề phân bổ tiếp theo.

Thậm chí nếu bạn ghi đè tiêu đề phân bổ tiếp theo (được sử dụng để quản lý khối bộ nhớ được phân bổ), nó sẽ không tự hiển thị vấn đề cho đến khi chủ sở hữu khối tiếp theo đó cố gắng trả lại nó.

1

Nhiều lần triển khai malloc lịch sử đặt dữ liệu sổ kế toán ngay trước và/hoặc sau khối được phân bổ. Có thể bạn đang ghi đè dữ liệu đó, trong trường hợp đó bạn sẽ không thấy bất kỳ lỗi/sự cố nào cho đến khi bạn cố giải phóng bộ nhớ (hoặc có thể miễn phí bất kỳ khối tiếp theo nào xảy ra). Tương tự như vậy, có thể thông tin sổ kế toán cho phân bổ tiếp theo sau đó sẽ ghi đè chuỗi của bạn.

Tôi nghi ngờ việc triển khai malloc hiện đại thực hiện một số nỗ lực để bảo vệ chống lại sự xâm nhập heap bằng phân bổ đệm với dữ liệu kiểm tra tính toàn vẹn, vì vậy nếu bạn may mắn, không có gì xấu xảy ra hoặc bạn có thể nhận được thông báo cảnh báo trong quá trình phân bổ sau này hoạt động.

0

Như đã nêu bởi những người khác, bạn hoàn toàn chính xác trong giả định rằng điều này là không tốt, và lý do bạn không thấy điều này là đệm. Hãy thử valgrind về điều này, điều này chắc chắn sẽ tìm thấy lỗi đó.

0

vấn đề thực sự của bạn là bạn đang viết

char* buffer = new char[strlen("This string is 27 char long" + 1)]; 

thay vì

char* buffer = new char[strlen("This string is 27 char long") + 1]; 

Nghĩa là vào ngày đầu tiên một bạn đang đem lại cho strlen() một địa chỉ mà không phải là bắt đầu chuỗi của bạn.

Hãy thử mã này:

const char szText[] = "This string is 27 char long"; 
char* buffer = new char[strlen(szText) + 1]; 
sprintf(buffer, szText); 
+0

Anh ấy đã biết điều đó.Đọc câu hỏi trước khi đăng câu trả lời. – manneorama

+1

Tôi biết vấn đề, đó là những gì nổi bật với tôi và đưa tôi đến đây. Đó là trong mã của người khác, tôi chỉ cần một ý kiến ​​thứ hai và như mong đợi kiến ​​thức bổ sung và cái nhìn sâu sắc ở đây đã được ngoạn mục. Cảm ơn sự giúp đỡ của bạn! – KevenK

+0

Bạn không trả lời câu hỏi. Anh ta biết rằng '+ 1' không đúng chỗ. Anh ta nói rõ ràng trong câu hỏi. Anh ta muốn biết liệu đánh giá của anh ta về lỗi có đúng không vì anh ta không thể tạo ra hành vi không chính xác với thử nghiệm. –

1

Tôi đã thử nó với phân bổ đống, các biến không liên tục trong bộ nhớ trong trường hợp này. Đó là lý do tại sao nó là khó khăn để làm cho tràn bộ đệm trong trường hợp này.

Mua thử nó với stack overflow

#include "stdio.h" 
#include "string.h" 

int main() 
{ 
    unsigned int y  = (0xFFFFFFFF); 
    char buffer[strlen("This string is 27 char long" + 1)]; 
     unsigned int x  = (0xFFFFFFFF); 
     sprintf(buffer, "This string is 27 char long"); 

     printf("X (%#x) is %#x, Y (%#x) is %#x, buffer '%s' (%#x) \n", &x, x,&y, y, buffer, buffer); 
     return 0; 
    } 

Bạn sẽ thấy rằng Y là hỏng.

0

Lý do chuỗi đang in tốt trong trình gỡ lỗi là một phần của sprintf, ký tự NULL sau được ghi vào bộ nhớ (trong trường hợp này vượt quá bộ đệm bạn đã cấp) và khi đọc chuỗi NULL ký tự có mặt để chấm dứt chuỗi như mong đợi.

Vấn đề là byte chứa ký tự NULL chưa được phân bổ như một phần của new ban đầu và do đó có thể được sử dụng cho một phân bổ khác sau này. Trong trường hợp này, khi bạn đến để đọc chuỗi sau đó bạn sẽ có khả năng nhận được chuỗi ban đầu của bạn với rác nối.

0

tuyên bố chính xác. Vì bạn đang chuyển địa chỉ của ký tự thứ hai của chuỗi tới strlen(), bạn sẽ nhận được độ dài ít hơn một ký tự. Bên cạnh đó, vấn đề chính là với sprintf(), đó là một trong những lý do khiến nó không an toàn.

Thậm chí biên dịch và thực hiện (cũng có thể bị lỗi).

char* x = new char; 
    sprintf(x, "This is way longer than one character"); 
    printf("%s", x); 

Để tránh vấn đề nguy hiểm này, bạn nên sử dụng phiên bản an toàn của chức năng này như snprintf() hoặc asprintf() thuộc GCC hoặc sprintf_s() dưới MSVC.

Là tài liệu tham khảo, vui lòng xem The GNU C Library documentation về vấn đề này và cũng lưu ý về bảo mật của bài viết sprintf() của MSDN.

Các vấn đề liên quan