什么是glibc*/malloc/realloc无效的下一个大小/无效指针错误以及如何修复它?

时间:2021-08-18 10:42:33

You are most likely seeing this question because your question has been closed as a duplicate of this. For a moderately complete list of related questions, please see A long list of possible duplicates — C memory allocation and overrunning bounds on Meta Stack Overflow.

你很可能会看到这个问题,因为你的问题是这个问题的重复。对于一个适度完整的相关问题列表,请参见可能的重复列表——元堆栈溢出上的C内存分配和越界。


Example Question

From free char*: invalid next size (fast) asked by noobie on 2014-04-11.

从免费字符*:无效的下一个尺寸(快)由noobie在2014-04-11问。

I am freeing a char* after a concatenation process, but I receive this error:

在连接过程之后,我正在释放一个char*,但是我收到了这个错误:

free(): invalid next size (fast): 0x0000000001b86170

This is my code:

这是我的代码:

void concat(stringList *list) {
    char *res = (char*)malloc(sizeof(char*));

    strcpy(res, list->head->string);

    list->tmp = list->head->next;
    while (list->tmp != NULL) {
        strcat(res, ",");
        strcat(res, list->tmp->string);
        list->tmp = list->tmp->next;
    }

    printf("%s\n", res);
    free(res);
}

Generic Question

When running my program, I see an error message like this:

在运行我的程序时,我看到这样一个错误消息:

*** glibc detected *** ./a.out: free(): corrupted unsorted chunks: 0x12345678 ***

The detailed information can contain any of the following after the *** glibc detected *** and the program name, and the message is followed by a hexadecimal address (shown as 0x12345678) and another ***:

详细信息可以包含*** glibc检测到*** *和程序名后的任何一个,消息后面跟着一个十六进制地址(显示为0x12345678)和另一个***:

  • free(): corrupted unsorted chunks: 0x12345678
  • free():已损坏的未排序块:0x12345678
  • free(): invalid next size (fast): 0x12345678
  • free():无效的next size (fast): 0x12345678
  • free(): invalid next size (normal): 0x12345678
  • free():无效的next size (normal): 0x12345678
  • free(): invalid pointer: 0x12345678
  • 免费():无效的指针:0 x12345678
  • free(): invalid size: 0x12345678
  • 免费():无效的大小:0 x12345678
  • malloc(): corrupted unsorted chunks: 0x12345678
  • malloc():损坏的未排序块:0x12345678
  • malloc(): corrupted unsorted chunks 2: 0x12345678
  • malloc():损坏的未排序块2:0x12345678
  • malloc(): memory corruption: 0x12345678
  • malloc():内存泄露:0 x12345678
  • malloc(): memory corruption (fast): 0x12345678
  • malloc():内存损坏(快速):0x12345678。
  • malloc(): smallbin double linked list corrupted: 0x12345678
  • malloc(): smallbin双链表损坏:0x12345678
  • munmap_chunk(): invalid pointer: 0x12345678
  • munmap_chunk():无效的指针:0 x12345678
  • realloc(): invalid next size (fast): 0x12345678
  • realloc():无效的next size (fast): 0x12345678
  • realloc(): invalid old size (fast): 0x12345678
  • realloc():无效的旧大小(快速):0x12345678
  • realloc(): invalid pointer: 0x12345678
  • realloc():无效的指针:0 x12345678
  • corrupted double-linked list: 0x12345678
  • 损坏的双链列表:0 x12345678

This happens while calling the frobnicate() function; what is wrong with that function?

这发生在调用frobnicate()函数时;这个函数有什么问题?

1 个解决方案

#1


12  

Answer for Example Question

unwind gave the accepted answer to the example question:

unwind给出了示例问题的公认答案:

Your code is wrong.

你的代码是错误的。

You are allocating space for a single pointer (malloc(sizeof(char*))), but no characters. You are overwriting your allocated space with all the strings, causing undefined behavior (in this particular case, corrupting malloc()'s book-keeping data).

您正在为单个指针分配空间(malloc(sizeof(char*))),但是没有字符。您正在用所有字符串覆盖已分配的空间,导致未定义的行为(在本例中,破坏malloc()的簿记数据)。

You don't need to allocate space for the pointer (res); it's a local variable. You must allocate space for all the characters you wish to store at the address held by the pointer.

不需要为指针分配空间;这是一个局部变量。您必须为希望存储在指针所在地址的所有字符分配空间。

Since you're going to be traversing a list to find strings to concatenate, you can't know the total size upfront. You're going to have to do two passes over the list: one to sum the strlen() of each string, then allocate that plus space for the separator and terminator, then another pass when you actually do the concatenation.

由于要遍历一个列表以查找要连接的字符串,所以不能预先知道总大小。您将需要对列表执行两次传递:一次对每个字符串的strlen()进行求和,然后为分隔符和终止符分配该+空间,然后在执行连接时进行另一次传递。

Generic Answer

What you are seeing is the result of a corruption in the internal structures of the glibc allocator. When you are allocating or freeing dynamic memory, the allocator has to manage the memory it reserved from the OS and, depending on the action requested by you, find a new chunk to hand out, sort a freed chunk into the list of those that it can hand out later again, or give the memory back to the operating system. These error messages show that the data structures it uses to manage this functionality are corrupted.

您现在看到的是glibc分配器内部结构中的损坏的结果。或释放动态分配内存时,分配器来管理内存保留从操作系统,根据动作要求你,找到一个新块手,释放块分类整理的列表,它可以再次分发后,或把记忆还给操作系统。这些错误消息表明,它用于管理此功能的数据结构已被破坏。

These errors all mean that some of your code has modified memory that it was not given to use, invoking undefined behaviour. This is most likely the result of overwriting some memory quite a bit earlier in your program, and it is totally possible that the error does not lie in the frobnicate() function.

这些错误都意味着您的一些代码修改了未使用的内存,调用了未定义的行为。这很可能是在您的程序中较早地覆盖一些内存的结果,而且完全有可能错误不在frobnicate()函数中。

Yes, this means that the error can be anywhere in your program or 3rd party libraries you use.

是的,这意味着错误可以在您的程序或第三方库中使用。

This is probably not a good question for Stack Overflow. Unless you have a good simple reproduction of your problem, this community may be unable to help you very much. The cause of the error can be anywhere in your code (and is very often not in the function where the error is spotted), and it may be in code that we cannot see. Stack Overflow is not a collaborative debugging site. Even when someone can find the flaw in your code, it is unlikely that your specific question will ever help any future visitor.

对于堆栈溢出来说,这可能不是一个好问题。除非你对你的问题有一个简单的复制,否则这个社区可能无法帮助你。错误的原因可能在代码中的任何地方(通常不在发现错误的函数中),也可能在我们看不到的代码中。Stack Overflow不是一个协作调试站点。即使有人能够在您的代码中找到缺陷,您的特定问题也不太可能帮助任何未来的访问者。

Common causes

  • Use after free. You have freed/deleted some memory and writing into it afterwards, overwriting the structures glibc needs for bookkeeping.
  • 使用后免费。您已经释放/删除了一些内存,然后写入其中,覆盖了glibc用于簿记的结构。
  • Off-by-N error. You are writing N bytes after an allocated chunk into unallocated memory that glibc uses internally for its bookkeeping.
  • Off-by-N错误。您正在将分配的块写入未分配的内存中N个字节,glibc在内部使用这些内存进行簿记。
  • Uninitialized pointers. You are not initializing a pointer. By coincidence it points to some memory reserved by glibc but not allocated by your program and you write to it.
  • 未初始化的指针。您没有初始化一个指针。巧合的是,它指向glibc保留的一些内存,但不是由程序分配的,您可以对其进行写入。
  • Allocating the wrong amount of space. This can be because you wrote long *data = malloc(number * 4) instead of long *data = malloc(number * sizeof(long)); or (better) long *data = malloc(number * sizeof(*data));. There are many other ways to get the size calculation wrong. Another common one is to forget to account for the null terminator character at the end of a string: char *copy = malloc(strlen(str)); instead of char *copy = malloc(strlen(str)+1);.
  • 分配错误的空间。这可能是因为您写入long *data = malloc(number * 4)而不是long *data = malloc(number * sizeof(long));或者(更好)long *data = malloc(number * sizeof(*data));有许多其他方法可以使大小计算错误。另一个常见的方法是忘记在字符串的末尾说明空终止符:char *copy = malloc(strlen(str));而不是char *copy = malloc(strlen(str)+1);

What you need to do now is to roll up your sleeves and debug that problem

现在需要做的是卷起袖子来调试这个问题

There is no simple answer what to look for, or what to fix. No single syntactical construct that you were using wrong. The cause of this bug can come in literally thousands of varieties.

没有简单的答案,也没有什么可以解决的。没有一个语法结构是你用错的。造成这种细菌的原因有成千上万种。

Tools

  • valgrind A tool created mostly for the purpose of finding exactly this kinds of errors. If it can't find anything make sure you are using the latest version, and you are also trying out the included exp-sgcheck tool. If you are running multithreaded code, the cause might also be related to a race condition so you might want to try the included race condition checkers drd and helgrind for more insight. At the point of writing this, valgrind supports the following platforms:
    • X86/Linux,
    • X86 / Linux,
    • AMD64/Linux,
    • AMD64 / Linux,
    • ARM/Linux,
    • 手臂/ Linux,
    • PPC32/Linux,
    • PPC32 / Linux,
    • PPC64/Linux,
    • PPC64 / Linux,
    • S390X/Linux,
    • S390X / Linux,
    • MIPS32/Linux,
    • MIPS32 / Linux,
    • MIPS64/Linux,
    • MIPS64 / Linux,
    • ARM/Android (2.3.x and later),
    • 手臂/ Android(2.3。x和更高版本),
    • X86/Android (4.0 and later),
    • X86 / Android(4.0及以后),
    • X86/Darwin and
    • X86 /达尔文和
    • AMD64/Darwin (Mac OS X 10.7, with limited support for 10.8).
    • AMD64/Darwin (Mac OS X 10.7,对10.8的支持有限)。
  • valgrind主要用于查找此类错误的工具。如果它找不到任何东西,请确保您正在使用最新的版本,并且您也正在试用包含在内的exp-sgcheck工具。如果您正在运行多线程代码,那么原因也可能与竞争条件有关,因此您可能想要尝试包含的竞争条件检查程序drd和helgrind,以获得更多的了解。在撰写本文时,valgrind支持以下平台:X86/Linux、AMD64/Linux、ARM/Linux、PPC32/Linux、PPC64/Linux、S390X/Linux、MIPS32/Linux、MIPS64/Linux、ARM/Android(2.3)。X86/Android(4.0及以上)、X86/Darwin和AMD64/Darwin (Mac OS x 10.7,对10.8的支持有限)。
  • purify A similar tool to valgrind, but commercial and aimed at a different set of platforms.
  • purify类似于valgrind的工具,但是它是商业的,并且针对不同的平台集。
  • AddressSanitizer A similar tool, but integrated into the compiler toolchain (gcc and clang).
  • AddressSanitizer类似的工具,但集成到了编译器工具链(gcc和clang)中。
  • efence A drop in allocator replacement that will try to crash your program earlier, so that you can find out with a normal debugger where the write to invalid memory happened.
  • 分配程序替换中的一个下降,它将尝试在更早的时候崩溃您的程序,这样您就可以通过一个普通的调试器找到发生写入无效内存的地方。
  • dmalloc a library with a similar purpose as efence.
  • dmalloc与efence具有类似用途的库。

Needing more assistance

If you can't solve your problem using one these tools, you should try to create an MCVE (How to create a Minimal, Complete, and Verifiable Example?) or, equivalently, an SSCCE (Short, Self Contained, Correct (Compilable), Example).

如果您不能使用这些工具来解决您的问题,您应该尝试创建MCVE(如何创建一个最小的、完整的、可验证的示例?)或者,等效地,创建一个SSCCE(短的、自包含的、正确的(可编译的)示例)。

Remember to work on a copy of your code because creating an MCVE requires you to ruthlessly remove code that does not help reproduce the problem. Using a VCS (version control system) to assist is a good idea; you can record intermediate stages in reducing the problem to a minimum. It might be a new throw-away repository just for reducing your problem to a manageable size.

记住要对代码进行复制,因为创建MCVE需要无情地删除不能帮助重现问题的代码。使用VCS(版本控制系统)协助是一个好主意;您可以记录中间阶段,将问题减少到最小。它可能是一个新的一次性存储库,只是为了将问题减少到可管理的大小。

With a good modular design to your code, it should be relatively easy to create the MCVE. Maybe you also already have a unit test that is better suited to be fed into one of the above tools. You also might just want to create one that can later serve as a regression test for this bug.

如果你的代码有一个好的模块化设计,那么创建MCVE应该相对容易些。也许您已经有了一个更适合于使用上述工具之一的单元测试。您可能还想创建一个稍后可以作为这个bug的回归测试的程序。

#1


12  

Answer for Example Question

unwind gave the accepted answer to the example question:

unwind给出了示例问题的公认答案:

Your code is wrong.

你的代码是错误的。

You are allocating space for a single pointer (malloc(sizeof(char*))), but no characters. You are overwriting your allocated space with all the strings, causing undefined behavior (in this particular case, corrupting malloc()'s book-keeping data).

您正在为单个指针分配空间(malloc(sizeof(char*))),但是没有字符。您正在用所有字符串覆盖已分配的空间,导致未定义的行为(在本例中,破坏malloc()的簿记数据)。

You don't need to allocate space for the pointer (res); it's a local variable. You must allocate space for all the characters you wish to store at the address held by the pointer.

不需要为指针分配空间;这是一个局部变量。您必须为希望存储在指针所在地址的所有字符分配空间。

Since you're going to be traversing a list to find strings to concatenate, you can't know the total size upfront. You're going to have to do two passes over the list: one to sum the strlen() of each string, then allocate that plus space for the separator and terminator, then another pass when you actually do the concatenation.

由于要遍历一个列表以查找要连接的字符串,所以不能预先知道总大小。您将需要对列表执行两次传递:一次对每个字符串的strlen()进行求和,然后为分隔符和终止符分配该+空间,然后在执行连接时进行另一次传递。

Generic Answer

What you are seeing is the result of a corruption in the internal structures of the glibc allocator. When you are allocating or freeing dynamic memory, the allocator has to manage the memory it reserved from the OS and, depending on the action requested by you, find a new chunk to hand out, sort a freed chunk into the list of those that it can hand out later again, or give the memory back to the operating system. These error messages show that the data structures it uses to manage this functionality are corrupted.

您现在看到的是glibc分配器内部结构中的损坏的结果。或释放动态分配内存时,分配器来管理内存保留从操作系统,根据动作要求你,找到一个新块手,释放块分类整理的列表,它可以再次分发后,或把记忆还给操作系统。这些错误消息表明,它用于管理此功能的数据结构已被破坏。

These errors all mean that some of your code has modified memory that it was not given to use, invoking undefined behaviour. This is most likely the result of overwriting some memory quite a bit earlier in your program, and it is totally possible that the error does not lie in the frobnicate() function.

这些错误都意味着您的一些代码修改了未使用的内存,调用了未定义的行为。这很可能是在您的程序中较早地覆盖一些内存的结果,而且完全有可能错误不在frobnicate()函数中。

Yes, this means that the error can be anywhere in your program or 3rd party libraries you use.

是的,这意味着错误可以在您的程序或第三方库中使用。

This is probably not a good question for Stack Overflow. Unless you have a good simple reproduction of your problem, this community may be unable to help you very much. The cause of the error can be anywhere in your code (and is very often not in the function where the error is spotted), and it may be in code that we cannot see. Stack Overflow is not a collaborative debugging site. Even when someone can find the flaw in your code, it is unlikely that your specific question will ever help any future visitor.

对于堆栈溢出来说,这可能不是一个好问题。除非你对你的问题有一个简单的复制,否则这个社区可能无法帮助你。错误的原因可能在代码中的任何地方(通常不在发现错误的函数中),也可能在我们看不到的代码中。Stack Overflow不是一个协作调试站点。即使有人能够在您的代码中找到缺陷,您的特定问题也不太可能帮助任何未来的访问者。

Common causes

  • Use after free. You have freed/deleted some memory and writing into it afterwards, overwriting the structures glibc needs for bookkeeping.
  • 使用后免费。您已经释放/删除了一些内存,然后写入其中,覆盖了glibc用于簿记的结构。
  • Off-by-N error. You are writing N bytes after an allocated chunk into unallocated memory that glibc uses internally for its bookkeeping.
  • Off-by-N错误。您正在将分配的块写入未分配的内存中N个字节,glibc在内部使用这些内存进行簿记。
  • Uninitialized pointers. You are not initializing a pointer. By coincidence it points to some memory reserved by glibc but not allocated by your program and you write to it.
  • 未初始化的指针。您没有初始化一个指针。巧合的是,它指向glibc保留的一些内存,但不是由程序分配的,您可以对其进行写入。
  • Allocating the wrong amount of space. This can be because you wrote long *data = malloc(number * 4) instead of long *data = malloc(number * sizeof(long)); or (better) long *data = malloc(number * sizeof(*data));. There are many other ways to get the size calculation wrong. Another common one is to forget to account for the null terminator character at the end of a string: char *copy = malloc(strlen(str)); instead of char *copy = malloc(strlen(str)+1);.
  • 分配错误的空间。这可能是因为您写入long *data = malloc(number * 4)而不是long *data = malloc(number * sizeof(long));或者(更好)long *data = malloc(number * sizeof(*data));有许多其他方法可以使大小计算错误。另一个常见的方法是忘记在字符串的末尾说明空终止符:char *copy = malloc(strlen(str));而不是char *copy = malloc(strlen(str)+1);

What you need to do now is to roll up your sleeves and debug that problem

现在需要做的是卷起袖子来调试这个问题

There is no simple answer what to look for, or what to fix. No single syntactical construct that you were using wrong. The cause of this bug can come in literally thousands of varieties.

没有简单的答案,也没有什么可以解决的。没有一个语法结构是你用错的。造成这种细菌的原因有成千上万种。

Tools

  • valgrind A tool created mostly for the purpose of finding exactly this kinds of errors. If it can't find anything make sure you are using the latest version, and you are also trying out the included exp-sgcheck tool. If you are running multithreaded code, the cause might also be related to a race condition so you might want to try the included race condition checkers drd and helgrind for more insight. At the point of writing this, valgrind supports the following platforms:
    • X86/Linux,
    • X86 / Linux,
    • AMD64/Linux,
    • AMD64 / Linux,
    • ARM/Linux,
    • 手臂/ Linux,
    • PPC32/Linux,
    • PPC32 / Linux,
    • PPC64/Linux,
    • PPC64 / Linux,
    • S390X/Linux,
    • S390X / Linux,
    • MIPS32/Linux,
    • MIPS32 / Linux,
    • MIPS64/Linux,
    • MIPS64 / Linux,
    • ARM/Android (2.3.x and later),
    • 手臂/ Android(2.3。x和更高版本),
    • X86/Android (4.0 and later),
    • X86 / Android(4.0及以后),
    • X86/Darwin and
    • X86 /达尔文和
    • AMD64/Darwin (Mac OS X 10.7, with limited support for 10.8).
    • AMD64/Darwin (Mac OS X 10.7,对10.8的支持有限)。
  • valgrind主要用于查找此类错误的工具。如果它找不到任何东西,请确保您正在使用最新的版本,并且您也正在试用包含在内的exp-sgcheck工具。如果您正在运行多线程代码,那么原因也可能与竞争条件有关,因此您可能想要尝试包含的竞争条件检查程序drd和helgrind,以获得更多的了解。在撰写本文时,valgrind支持以下平台:X86/Linux、AMD64/Linux、ARM/Linux、PPC32/Linux、PPC64/Linux、S390X/Linux、MIPS32/Linux、MIPS64/Linux、ARM/Android(2.3)。X86/Android(4.0及以上)、X86/Darwin和AMD64/Darwin (Mac OS x 10.7,对10.8的支持有限)。
  • purify A similar tool to valgrind, but commercial and aimed at a different set of platforms.
  • purify类似于valgrind的工具,但是它是商业的,并且针对不同的平台集。
  • AddressSanitizer A similar tool, but integrated into the compiler toolchain (gcc and clang).
  • AddressSanitizer类似的工具,但集成到了编译器工具链(gcc和clang)中。
  • efence A drop in allocator replacement that will try to crash your program earlier, so that you can find out with a normal debugger where the write to invalid memory happened.
  • 分配程序替换中的一个下降,它将尝试在更早的时候崩溃您的程序,这样您就可以通过一个普通的调试器找到发生写入无效内存的地方。
  • dmalloc a library with a similar purpose as efence.
  • dmalloc与efence具有类似用途的库。

Needing more assistance

If you can't solve your problem using one these tools, you should try to create an MCVE (How to create a Minimal, Complete, and Verifiable Example?) or, equivalently, an SSCCE (Short, Self Contained, Correct (Compilable), Example).

如果您不能使用这些工具来解决您的问题,您应该尝试创建MCVE(如何创建一个最小的、完整的、可验证的示例?)或者,等效地,创建一个SSCCE(短的、自包含的、正确的(可编译的)示例)。

Remember to work on a copy of your code because creating an MCVE requires you to ruthlessly remove code that does not help reproduce the problem. Using a VCS (version control system) to assist is a good idea; you can record intermediate stages in reducing the problem to a minimum. It might be a new throw-away repository just for reducing your problem to a manageable size.

记住要对代码进行复制,因为创建MCVE需要无情地删除不能帮助重现问题的代码。使用VCS(版本控制系统)协助是一个好主意;您可以记录中间阶段,将问题减少到最小。它可能是一个新的一次性存储库,只是为了将问题减少到可管理的大小。

With a good modular design to your code, it should be relatively easy to create the MCVE. Maybe you also already have a unit test that is better suited to be fed into one of the above tools. You also might just want to create one that can later serve as a regression test for this bug.

如果你的代码有一个好的模块化设计,那么创建MCVE应该相对容易些。也许您已经有了一个更适合于使用上述工具之一的单元测试。您可能还想创建一个稍后可以作为这个bug的回归测试的程序。