2012-08-31 41 views
6

Có một số bộ đệm nhị phân với kích thước cố định trong một chương trình được sử dụng để lưu trữ dữ liệu. Và memcpy được sử dụng để sao chép bộ đệm từ bộ đệm này sang bộ đệm khác. Vì bộ đệm nguồn có thể lớn hơn bộ đệm đích. Làm thế nào tôi có thể phát hiện nếu có tràn bộ đệm?Làm thế nào để ngăn chặn tràn bộ đệm memcpy?

+1

Phát hiện? Bạn có biết kích thước bộ đệm đích không? Sau đó viết mã như thế này memcpy (src, dst, sizeof (dst)) – BSen

+0

So sánh kích thước của bộ đệm nguồn và bộ đệm đích và xem cái nào lớn hơn? – SingerOfTheFall

+1

@BSen rằng '' sizeof'' sẽ chỉ cho kích thước của một con trỏ. – juanchopanza

Trả lời

8

Bạn phải biết có bao nhiêu dữ liệu trong bộ đệm nguồn và lượng bộ nhớ có sẵn trong bộ đệm mục tiêu.

Không gọi memcpy() nếu không có đủ dung lượng trong bộ đệm đích cho tất cả dữ liệu bạn muốn sao chép từ bộ đệm nguồn. (Bạn phải quyết định xem có cắt ngắn dữ liệu nếu nguồn lớn hơn mục tiêu không.)

Nếu bạn không biết, hãy viết lại mã để biết số lượng không gian là bao nhiêu; nếu không, nó không an toàn.

Lưu ý rằng nếu có cơ hội đệm nguồn và đích nhắm chồng lên nhau, bạn nên sử dụng memmove() thay vì memcpy().

Trong C++, hãy xem xét yêu cầu sử dụng memcpy() ngay từ đầu; đó là một hoạt động kiểu C chứ không phải là C++.

+0

Cảm ơn. cách thích hợp để làm bản sao bộ nhớ trong C + + là gì? –

+1

@MichaelD: Lưu trữ dữ liệu của bạn trong 'std :: vector <>' và chỉ sử dụng 'vector2 = vector1'. – MSalters

+0

Làm cách nào để chèn dữ liệu vào vectơ? sử dụng push_back để chèn byte ngày theo byte? –

3

Bạn nên luôn luôn biết và kiểm tra kích thước bộ đệm src và dest!

void *memcpy(void *dest, const void *src, size_t n); 

n không bao giờ nên vĩ đại hơn src hoặc dest kích thước.

0

Nếu ví dụ bạn có:

điểm đến 4 byte kích thước

nguồn 5 byte kích thước

Bạn có thể chắc chắn để sao chép, ít nhất, 4 byte để đệm điểm:

size_t getCopySize(size_t sourceSize, size_t destSize) 
{ 
    return (destSize <= sourceSize ? destSize : sourceSize); 
} 
memcpy(destination, source, getCopySize(sizeof(source),sizeof(destination))); 

Căn cứ vào ứng dụng của bạn, bạn cũng có thể đảm bảo rằng dữ liệu còn lại sẽ được sao chép sau đó hoặc bạn có thể bỏ qua nếu một số dữ liệu có thể bị bỏ qua.

4

Làm cách nào để phát hiện xem có tràn bộ đệm không?

Tôi nghĩ bạn có ba hoặc bốn lựa chọn (cho hoặc nhận).


Lựa chọn đầu tiên là cung cấp chức năng "an toàn" cho memcpy. Đây là những gì tôi yêu cầu trong mã theo tầm nhìn của tôi, và tôi thường xuyên kiểm tra nó. Tôi cũng yêu cầu tất cả các thông số được xác nhận, và tất cả các thông số được xác nhận.

Các xác nhận tạo mã tự gỡ lỗi. Tôi muốn các nhà phát triển viết mã; và tôi không muốn họ lãng phí thời gian gỡ lỗi. Vì vậy, tôi yêu cầu họ viết mã tự gỡ lỗi. ASSERT cũng ghi lại mọi thứ khá tốt, vì vậy họ có thể tiết kiệm tài liệu. Trong bản phát hành bản phát hành, các ASSERT được loại bỏ bằng các macro tiền xử lý trước.

errno_t safe_memcpy(void* dest, size_t dsize, void* src, size_t ssize, size_t cnt) 
{ 
    ASSERT(dest != NULL); 
    ASSERT(src != NULL); 
    ASSERT(dsize != 0); 
    ASSERT(ssize != 0); 
    ASSERT(cnt != 0); 

    // What was the point of this call? 
    if(cnt == 0) 
     retrn 0; 

    if(dest == NULL || src == NULL) 
     return EINVALID; 

    if(dsize == 0 || ssize == 0) 
     return EINVALID; 

    ASSERT(dsize <= RSIZE_MAX); 
    ASSERT(ssize <= RSIZE_MAX); 
    ASSERT(cnt <= RSIZE_MAX); 

    if(dsize > RSIZE_MAX || ssize > RSIZE_MAX || cnt > RSIZE_MAX) 
     return EINVALID; 

    size_t cc = min(min(dsize, ssize), cnt); 
    memmove(dest, src, cc); 

    if(cc != cnt) 
     return ETRUNCATE; 

    return 0; 
} 

Nếu số safe_memcpy trả về 0, thì có lỗi như thông số không đúng hoặc tràn bộ đệm tiềm ẩn.


Lựa chọn thứ hai là sử dụng các chức năng "an toàn hơn" được cung cấp bởi tiêu chuẩn C. C có chức năng "an toàn hơn" qua ISO/IEC TR 24731-1, Bounds Checking Interfaces. Trên nền tảng phù hợp, bạn có thể chỉ cần gọi gets_ssprintf_s. Chúng cung cấp hành vi nhất quán (như luôn đảm bảo một chuỗi là NULL chấm dứt) và các giá trị trả về nhất quán (như 0 thành công hoặc errno_t).

errno_t err = memcpy_s(dest, dsize, src, cnt); 
... 

Thật không may, gcc và glibc không tuân thủ tiêu chuẩn C. Ulrich Drepper (một trong những người bảo trì glibc) được gọi là các giao diện kiểm tra giới hạn "horribly inefficient BSD crap" và chúng chưa bao giờ được thêm vào.


Lựa chọn thứ ba là sử dụng giao diện "an toàn hơn" của nền tảng, nếu có. Trên Windows, điều đó xảy ra giống như trong ISO/IEC TR 24731-1, Bounds Checking Interfaces. Bạn cũng có thư viện String Safe.

Trên Apple và BSD, bạn không có chức năng "an toàn hơn" cho memcpy. Nhưng bạn có các chức năng chuỗi an toàn hơn như strlcpy, strlcat và bạn bè.


Trên Linux, lựa chọn thứ tư của bạn là sử dụng FORTIFY_SOURCE. FORTIFY_SOURCE sử dụng các biến thể "an toàn hơn" của các hàm có nguy cơ cao như memcpy, strcpygets. Trình biên dịch sử dụng các biến thể an toàn hơn khi nó có thể suy ra kích thước bộ đệm đích. Nếu bản sao sẽ vượt quá kích thước bộ đệm đích, thì chương trình sẽ gọi số abort(). Nếu trình biên dịch không thể suy ra kích thước bộ đệm đích, thì các biến thể "an toàn hơn" không được sử dụng.

Để tắt FORTIFY_SOURCE để thử nghiệm, bạn nên biên dịch chương trình với -U_FORTIFY_SOURCE hoặc -D_FORTIFY_SOURCE=0.

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