Tại sao tôi gặp lỗi khẳng định C malloc?


85

Tôi đang triển khai một thuật toán đa thức chia và chinh phục để tôi có thể so sánh nó với việc triển khai OpenCL, nhưng tôi không thể bắt mallocđầu. Khi tôi chạy chương trình, nó sẽ phân bổ một loạt nội dung, kiểm tra một số thứ, sau đó gửi size/2đến thuật toán. Sau đó, khi tôi nhấn vào mallocdòng một lần nữa, nó phát ra điều này:

malloc.c:3096: sYSMALLOc: Assertion `(old_top == (((mbinptr) (((char *) &((av)->bins[((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size) >= (unsigned long)((((__builtin_offsetof (struct malloc_chunk, fd_nextsize))+((2 * (sizeof(size_t))) - 1)) & ~((2 * (sizeof(size_t))) - 1))) && ((old_top)->size & 0x1) && ((unsigned long)old_end & pagemask) == 0)' failed.
Aborted

Dòng được đề cập là:

int *mult(int size, int *a, int *b) {
    int *out,i, j, *tmp1, *tmp2, *tmp3, *tmpa1, *tmpa2, *tmpb1, *tmpb2,d, *res1, *res2;
    fprintf(stdout, "size: %d\n", size);

    out = (int *)malloc(sizeof(int) * size * 2);
}

Tôi đã kiểm tra kích thước bằng a fprintfvà nó là một số nguyên dương (thường là 50 tại thời điểm đó). Tôi cũng đã thử gọi mallocbằng một số đơn giản và tôi vẫn gặp lỗi. Tôi chỉ bối rối với những gì đang xảy ra và không có gì từ Google mà tôi thấy cho đến nay là hữu ích.

Bất kỳ ý tưởng những gì đang xảy ra? Tôi đang cố gắng tìm cách biên dịch GCC mới hơn trong trường hợp đó là lỗi trình biên dịch, nhưng tôi thực sự nghi ngờ điều đó.


tôi nghi ngờ vấn đề thực sự là một dòng trước đó. Có lẽ một đôi miễn phí?
Mitch Wheat

Dòng thứ 3 trong chương trình: int * mult (int size, int * a, int * b) {int * out, i, j, * tmp1, * tmp2, * tmp3, * tmpa1, * tmpa2, * tmpb1, * tmpb2 , d, * res1, * res2; fprintf (stdout, "size:% d \ n", size); out = (int *) malloc (sizeof (int) * size * 2);
Chris

Câu trả lời:


98

99,9% khả năng là bạn đã bị hỏng bộ nhớ (bộ đệm quá tải hoặc thiếu dòng, được ghi vào một con trỏ sau khi nó được giải phóng, được gọi là trống hai lần trên cùng một con trỏ, v.v.)

Chạy mã của bạn trong Valgrind để xem chương trình của bạn đã làm sai ở đâu.


1
đã sửa. Valgrind chắc chắn đã giúp. Tôi đã sao chép sai mã matlab cũ của mình và có một vòng lặp for lặp lại trên j, sau đó bên trong nó có j ++ mà hầu hết đã ghi đè lên mảng mà nó đang viết và bằng cách nào đó khiến cho malloc bị lỗi. cảm ơn đã giúp đỡ!
Chris

Valgrind chỉ là công cụ tôi cần để tìm hiểu điều gì đang xảy ra khi tôi gặp lỗi này. Cảm ơn vì đã đề cập đến nó.
alexwells

77

Để bạn hiểu rõ hơn về lý do tại sao điều này xảy ra, tôi muốn mở rộng một chút câu trả lời của @ r-samuel-klatchko.

Khi bạn gọi malloc, những gì đang thực sự xảy ra phức tạp hơn một chút so với việc bạn chỉ cung cấp cho bạn một phần bộ nhớ để chơi. Bên dưới mui xe, malloccũng lưu giữ một số thông tin quản lý về bộ nhớ mà nó đã cung cấp cho bạn (quan trọng nhất là kích thước của nó), để khi bạn gọi free, nó sẽ biết những thứ như còn bao nhiêu bộ nhớ. Thông tin này thường được lưu giữ ngay trước khi vị trí bộ nhớ được trả lại cho bạn malloc. Thông tin đầy đủ hơn có thể được tìm thấy trên internet ™ , nhưng ý tưởng cơ bản (rất) là như sau:

+------+-------------------------------------------------+
+ size |                  malloc'd memory                +
+------+-------------------------------------------------+
       ^-- location in pointer returned by malloc

Dựa trên điều này (và đơn giản hóa mọi thứ rất nhiều), khi bạn gọi malloc, nó cần có một con trỏ đến phần tiếp theo của bộ nhớ có sẵn. Một cách rất đơn giản để làm điều này là xem xét bit bộ nhớ trước đó nó đã cho đi và di chuyển các sizebyte xuống sâu hơn (hoặc lên trên) trong bộ nhớ. Với việc thực hiện này, bạn kết thúc với bộ nhớ bạn đang tìm kiếm một cái gì đó như thế này sau khi phân bổ p1, p2p3:

+------+----------------+------+--------------------+------+----------+
+ size |                | size |                    | size |          +
+------+----------------+------+--------------------+------+----------+
       ^- p1                   ^- p2                       ^- p3

Vì vậy, những gì đang gây ra lỗi của bạn?

Hãy tưởng tượng rằng mã của bạn ghi sai số lượng bộ nhớ mà bạn đã cấp phát (có thể là do bạn cấp phát ít hơn mức cần thiết cũng như vấn đề của bạn hoặc do bạn đang sử dụng sai các điều kiện biên ở đâu đó trong mã của mình). Giả sử mã của bạn ghi dữ liệu rất nhiều điều để p2mà nó bắt đầu ghi đè lên những gì có trong p3's sizelĩnh vực. Khi bạn thực hiện cuộc gọi tiếp theo malloc, nó sẽ xem xét vị trí bộ nhớ cuối cùng mà nó trả về, nhìn vào trường kích thước của nó, di chuyển đến p3 + sizevà sau đó bắt đầu cấp phát bộ nhớ từ đó. sizeTuy nhiên, vì mã của bạn đã bị ghi đè , vị trí bộ nhớ này không còn sau bộ nhớ được cấp phát trước đó.

Không cần phải nói, điều này có thể tàn phá! Do đó, những người thực hiện mallocđã đưa vào một số "xác nhận", hoặc kiểm tra, cố gắng thực hiện một loạt các kiểm tra tỉnh táo để nắm bắt điều này (và các vấn đề khác) nếu chúng sắp xảy ra. Trong trường hợp cụ thể của bạn, những xác nhận này bị vi phạm và do đó bị mallochủy bỏ, cho bạn biết rằng mã của bạn sắp làm điều gì đó mà nó thực sự không nên làm.

Như đã nói trước đây, đây là một sự đơn giản hóa thô thiển, nhưng nó đủ để minh họa quan điểm. Việc triển khai glibc malloclà hơn 5k dòng và đã có một lượng lớn nghiên cứu về cách xây dựng cơ chế cấp phát bộ nhớ động tốt, vì vậy không thể bao gồm tất cả trong một câu trả lời SO. Hy vọng rằng điều này đã cho bạn một chút cái nhìn về những gì thực sự gây ra sự cố!


14

Giải pháp thay thế của tôi để sử dụng Valgrind:

Tôi rất vui vì tôi vừa giúp bạn tôi gỡ lỗi một chương trình. Chương trình của anh ấy gặp sự cố chính xác này ( malloc()gây ra sự hủy bỏ), với cùng một thông báo lỗi từ GDB.

Tôi đã biên soạn chương trình của anh ấy bằng cách sử dụng Address Sanitizer với

gcc -Wall -g3 -fsanitize=address -o new new.c
              ^^^^^^^^^^^^^^^^^^

Và sau đó chạy gdb new. Khi chương trình bị chấm dứt do nguyên SIGABRTnhân sau đó malloc(), toàn bộ thông tin hữu ích sẽ được in ra:

=================================================================
==407==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6060000000b4 at pc 0x7ffffe49ed1a bp 0x7ffffffedc20 sp 0x7ffffffed3c8
WRITE of size 104 at 0x6060000000b4 thread T0
    #0 0x7ffffe49ed19  (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x5ed19)
    #1 0x8001dab in CreatHT2 /home/wsl/Desktop/hash/new.c:59
    #2 0x80031cf in main /home/wsl/Desktop/hash/new.c:209
    #3 0x7ffffe061b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    #4 0x8001679 in _start (/mnt/d/Desktop/hash/new+0x1679)

0x6060000000b4 is located 0 bytes to the right of 52-byte region [0x606000000080,0x6060000000b4)
allocated by thread T0 here:
    #0 0x7ffffe51eb50 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb50)
    #1 0x8001d56 in CreatHT2 /home/wsl/Desktop/hash/new.c:55
    #2 0x80031cf in main /home/wsl/Desktop/hash/new.c:209
    #3 0x7ffffe061b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)

Hãy xem kết quả đầu ra, đặc biệt là dấu vết ngăn xếp:

Phần đầu tiên cho biết có một thao tác ghi không hợp lệ tại new.c:59. Dòng đó đọc

memset(len,0,sizeof(int*)*p);
             ^^^^^^^^^^^^

Phần thứ hai nói rằng bộ nhớ mà việc viết xấu đã xảy ra được tạo ra tại new.c:55. Dòng đó đọc

if(!(len=(int*)malloc(sizeof(int)*p))){
                      ^^^^^^^^^^^

Đó là nó. Tôi chỉ mất chưa đầy nửa phút để xác định vị trí lỗi khiến bạn tôi bối rối trong vài giờ. Anh ấy đã tìm cách xác định được lỗi, nhưng lần malloc()gọi tiếp theo không thành công mà không thể phát hiện ra lỗi này trong mã trước đó.

Tổng kết: Hãy thử -fsanitize=addressGCC hoặc Clang. Nó có thể rất hữu ích khi gỡ lỗi các vấn đề về bộ nhớ.


1
Bạn vừa cứu mạng tôi.
Nate Symer

2

Bạn có thể đang chạy quá mức vượt quá mem được phân bổ ở đâu đó. sau đó sw bên dưới sẽ không nhận nó cho đến khi bạn gọi malloc

Có thể có một giá trị bảo vệ bị che khuất đang bị bắt bởi malloc.

chỉnh sửa ... đã thêm cái này để được trợ giúp kiểm tra giới hạn

http://www.lrde.epita.fr/~akim/ccmp/doc/bounds-checking.html


2

Tôi nhận được tin nhắn sau, tương tự như tin nhắn của bạn:

    program: malloc.c: 2372: sysmalloc: Assertion `(old_top == (((mbinptr) (((char *) & ((av) -> bins [((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size)> = (unsigned long) (((__ builtin_offsetof (struct malloc_chunk, fd_nextsize)) + ((2 * (sizeof (size_t))) - 1)) & ~ ((2 * (sizeof) (size_t))) - 1))) && ((old_top) -> size & 0x1) && ((unsigned long) old_end & pagemask) == 0) 'không thành công.

Đã mắc lỗi một số lệnh gọi phương thức trước đây, khi sử dụng malloc. Đã ghi đè sai dấu nhân '*' bằng dấu '+', khi cập nhật thừa số sau toán tử sizeof () - khi thêm trường vào mảng char không dấu.

Đây là mã chịu trách nhiệm cho lỗi trong trường hợp của tôi:

    UCHAR * b = (UCHAR *) malloc (sizeof (UCHAR) +5);
    b [INTBITS] = (một số phép tính);
    b [BUFSPC] = (một số phép tính);
    b [BUFOVR] = (một số phép tính);
    b [BUFMEM] = (một số phép tính);
    b [MATCHBITS] = (một số phép tính);

Trong một phương pháp khác sau đó, tôi đã sử dụng lại malloc và nó tạo ra thông báo lỗi được hiển thị ở trên. Cuộc gọi là (đủ đơn giản):

    UCHAR * b = (UCHAR *) malloc (sizeof (UCHAR) * 50);

Hãy nghĩ đến việc sử dụng dấu '+' - trong lần gọi đầu tiên, dẫn đến tính toán sai kết hợp với việc khởi tạo ngay mảng sau (ghi đè bộ nhớ không được cấp cho mảng), đã gây ra một số nhầm lẫn cho bản đồ bộ nhớ của malloc. Do đó, cuộc gọi thứ 2 đã bị sai.


0

Chúng tôi gặp lỗi này vì chúng tôi quên nhân với sizeof (int). Lưu ý rằng đối số cho malloc (..) là một số byte, không phải số từ máy hay bất cứ thứ gì.


0

tôi gặp vấn đề tương tự, tôi đã sử dụng lại malloc hơn n trong một vòng lặp để thêm dữ liệu chuỗi char * mới. tôi gặp phải vấn đề tương tự, nhưng sau khi giải phóng void free()vấn đề bộ nhớ được cấp phát đã được sắp xếp


-2

Tôi đã chuyển một ứng dụng từ Visual C sang gcc qua Linux và tôi gặp vấn đề tương tự với

malloc.c: 3096: sYSMALLOc: Khẳng định bằng gcc trên UBUNTU 11.

Tôi đã di chuyển mã tương tự sang bản phân phối Suse (trên máy tính khác) và tôi không gặp vấn đề gì.

Tôi nghi ngờ rằng các vấn đề không nằm trong chương trình của chúng tôi mà nằm trong libc của riêng chúng tôi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.