首页 > C++编程 > 调试器也有MagicNumer——从一个奇葩的错误说起

调试器也有MagicNumer——从一个奇葩的错误说起

最近太忙,博客也好久没有打理了,主要是没有东西可写,呵呵~

写代码的时候,在delete的时候遇到一个错误,提示“T.exe 中的 0x002a1614 处未处理的异常: 0xC0000005: 读取位置 0xfeeefeee 时发生访问冲突”,立刻就被其中的0xFEEEFEEE吸引住了,显然,这样的地址在用户空间(User Space)是不可能用到的,刚好前不久看到一条微博说调试器也有Magic Number,利用16进制构造出一些很有意思的数据,于是马上Google了一下0xFEEEFEEE,果然,Windows上的堆管理器会给delete掉后的内存空间填充0xFEEEFEEE,也就是说,在出现这个异常的时候,已经是第二次delete了。

平时写代码的时候,我并没有给delete之后的指针再次赋值为NULL的习惯,但是这次,加上这个动作也不行了,后来仔细一看,还是指针参数传递造成的一个误区。先看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
 
const int nMaxSize = 26;
struct Node
{
    Node *next[nMaxSize];
    Node()
    {
        for (int i = 0; i < nMaxSize; ++i)
        {
            next[i] = NULL;
        }
    }
};
Node root;
 
void Insert(char *pStr)
{
    Node *p = &root;
    for (int i = 0; i < strlen(pStr); ++i)
    {
        int id = pStr[i] - 'a';
        if (p->next[id] != NULL)
        {
            printf("test log\n");
        }
        else
        {
            p->next[id] = new Node();
        }
    }
}
 
void Delete(Node *p)
{
    if (p == NULL) return ;
    for (int i = 0; i < nMaxSize; ++i)
    {
        Delete(p->next[i]);
    }
    if (p == &root) return ;
    delete p;
    p = NULL;
}
 
int main(int argc, char **argv)
{
    printf("Insert a string\n");
    Insert("a");
    Delete(&root);
    printf("Insert the same string again\n");
    Insert("a");
    Delete(&root);
 
    return 0;
}

示例代码,大概意思是建立了一个字典树(无关的代码都省了),然后在main中插入一个字符串”a”,接着删除字典树,再次插入字符串”a”,再次删除的时候,调用Delete函数,就会触发异常了。

异常分析:给字典树插入字符串的时候,根节点的26个子节点都是NULL,这时候插入a,将在第0个节点分配一块内存。删除的时候通过将根节点的地址传递给Delete()函数,确实是将分配的内存delete掉了。然后你还看到我特意加了一句空指针赋值(第47行代码),值得注意的时候,这一句并没有起到我想要的作用。以至于在第二次插入字符串a的时候,第0个节点并不是NULL,所以我们看到执行了printf输出log,也没有去分配新的内存了。

明明在第47行进行了NULL赋值,为什么进入了第27行的if语句?因为通过Delete()函数传入的指针只不过是一个临时变量而已,之所以能通过delete删除内存块,是因为他确确实实指向了用new分配的节点内存块,但是他不是root节点的成员(也就是next数组中的第一个元素),所以我们只是讲一个临时的指针指向了NULL,毫无意义。

接下来,就是奇迹发生的时刻了。因为root的next数组第一个成员仍然指向内存块(最主要的是这个内存块已经被delete掉了),自然就通过了if语句的非空判断,这里就没有继续分配内存了,只是因为这里并没有对这个指针进行读写操作,所以直到delete的时候才发生了异常

我的解决方法是,调用Delete()之后,通过memset对root进行清零操作。

奇葩的错误到此为止,下面进入文章的主题,调试器也有MagicNumer

Many computer processors, operating systems, and debuggers make use of magic numbers, especially as a magic debug value.
0x0000000FF1CE ("office") is used as the last part of product codes (guid) for Microsoft Office components (visible in registry under HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall registry key).
0x00BAB10C ("oo-ba-block") is used as the magic number for the ZFS uberblock.
0x8BADF00D ("ate bad food") is used by Apple in iOS crash reports, when an application takes too long to launch, terminate, or respond to system events.[1]
0x1BADB002 ("1 bad boot"[2]) Multiboot header magic number.[3]
0x1CEB00DA ("ice buddha") was used as the origin for the binary file parser IceBuddha.[4]
0xB16B00B5 ("big boobs") was required by Microsoft's Hyper-V hypervisor to be used by Linux guests as their "guest signature".[5] This offending code was later changed to 0x0DEFACED ("defaced").[6]
0xBAADF00D ("bad food") is used by Microsoft's LocalAlloc(LMEM_FIXED) to indicate uninitialised allocated heap memory when the debug heap is used.[7]
0xBADDCAFE ("bad cafe") is used by Libumem to indicate uninitialized memory area
C15C:0D06:F00D ("cisco dog food") used in the IPv6 address of www.cisco.com on World IPv6 Day. "Dog food" refers to Cisco eating its own dog food with IPv6.
0xCAFEBABE ("cafe babe") is used by Mach-O to identify Universal object files, and by the Java programming language to identify Java bytecode class files.[8]
0xCAFED00D ("cafe dude") is used by Java as a magic number for their pack200 compression.[9]
0xCEFAEDFE ("face feed") is used by Mach-O to identify flat (single architecture) object files. In little endian this reads FEEDFACE, "Feed Face".
0xD15EA5E ("disease") is a flag that indicates regular boot on the Nintendo GameCube and Wii consoles.[10][11]
0xDABBAD00 ("dabba doo") is the name of a blog on computer security.[12]
0xDEADBABE ("Dead Babe") is used by IBM Jikes RVM as a sanity check of the stack of the primary thread [13]
0xDEADBEAF ("dead beaf") is part of the signature code of Jazz Jackrabbit 2 tileset files.[14] Level files have less room for their signatures and use 0xBABE ("babe") instead.[15]
0xDEADBEEF ("dead beef") is frequently used to indicate a software crash or deadlock in embedded systems. DEADBEEF was originally used to mark newly allocated areas of memory that had not yet been initialized -- when scanning a memory dump, it is easy to see the DEADBEEF. It is used by IBM RS/6000 systems, Mac OS on 32-bit PowerPC processors and the Commodore Amiga as a magic debug value. On Sun Microsystems' Solaris, it marks freed kernel memory. On OpenVMS running on Alpha processors, DEAD_BEEF can be seen by pressing CTRL-T. The DEC Alpha SRM console has a background process that traps memory errors, identified by PS as "BeefEater waiting on 0xdeadbeef".[16]
0xDEADC0DE ("dead code") is used as a marker in OpenWrt firmware to signify the beginning of the to-be created jffs2 filesystem at the end of the static firmware.
0xDEADDEAD ("dead dead") is the bug check (STOP) code displayed when invoking a Blue Screen of Death either by telling the kernel via the attached debugger, or by using a special keystroke combination.[17] This is usually seen by driver developers, as it is used to get a memory dump on Windows NT based systems. An alternative to 0xDEADDEAD is the bug check code 0x000000E2,[18] as they are both called MANUALLY_INITIATED_CRASH as seen on the Microsoft Developer Network.
0xDEADD00D ("dead dude") is used by Android in the Dalvik virtual machine to indicate a VM abort.
0xDEADFA11 ("dead fall") is used by Apple in iOS crash reports, when the user force quits an application.[1]
0xDEAD10CC ("dead lock") is used by Apple in iOS crash reports, when application holds on to a system resource while running in the background.[1]
0xDEFEC8ED ("defecated") is the magic number for OpenSolaris core dumps.[19]
0xE011CFD0 is used as magic number for Microsoft Office files. In little endian this reads D0CF11E0, "docfile0".[20]
face:b00c ("facebook") used in the IPv6 address of www.v6.facebook.com
0xFACEFEED ("face feed") is used by Alpha servers running Windows NT. The Alpha Hardware Abstraction Layer (HAL) generates this error when it encounters a hardware failure.[21]
0xFEE1DEAD ("feel dead") is used as a magic number in the Linux reboot system call.[22]
0xDEADBAAD ("dead bad") is used by the Android libc abort() function when native heap corruption is detected.
deadbeef-dead-beef-dead-beef00000075("dead beef") is the GUID assigned to hung/dead virtual machines in Citrix Xenserver

第二个表格:

..FACADE	"Facade", Used by a number of RTOSes
1BADB002	"Bad booze", Multiboot header magic number
A5A5A5A5	Used in embedded development because the alternating bit pattern (1010 0101) creates an easily recognized pattern on oscilloscopes and logic analyzers.
A5	Used in FreeBSD's PHK malloc(3) for debugging when /etc/malloc.conf is symlinked to "-J" to initialize all newly allocated memory as this value is not a NULL pointer or ASCII NUL character.
ABABABAB	Used by Microsoft's HeapAlloc() to mark "no man's land" guard bytes after allocated heap memory
ABADBABE	"A bad babe", Used by Apple as the "Boot Zero Block" magic number
ABADCAFE	"A bad cafe", Used to initialize all unallocated memory (Mungwall, AmigaOS).
0DEFACED	"Defaced", Required by Microsoft's Hyper-V hypervisor to be used by Linux guests as their "guest signature", after changing from original 0xB16B00B5
BAADF00D	"Bad food", Used by Microsoft's LocalAlloc(LMEM_FIXED) to mark uninitialized allocated heap memory
BADBADBADBAD	"Bad bad bad bad", Burroughs large systems "uninitialized" memory (48-bit words)
BADC0FFEE0DDF00D	"Bad coffee odd food", Used on IBM RS/6000 64-bit systems to indicate uninitialized CPU registers
BADDCAFE	"Bad cafe", On Sun Microsystems' Solaris, marks uninitialised kernel memory (KMEM_UNINITIALIZED_PATTERN)
BBADBEEF	"Bad beef", Used in WebKit[clarification needed]
BEEFCACE	"Beef cake", Used by Microsoft .NET as a magic number in resource files
CAFED00D	"Cafe dude", Used by Java for their pack200 compression
CAFEFEED	"Cafe feed", Used by Sun Microsystems' Solaris debugging kernel to mark kmemfree() memory
CCCCCCCC	Used by Microsoft's C++ debugging runtime library and many DOS environments to mark uninitialized stack memory. CC resembles the opcode of the INT 3 debug breakpoint interrupt on x86 processors.
CDCDCDCD	Used by Microsoft's C++ debugging runtime library to mark uninitialized heap memory
D15EA5E	"Disease", Used as a flag to indicate regular boot on the Nintendo GameCube and Wii consoles
DDDDDDDD	Used by MicroQuill's SmartHeap and Microsoft's C++ debugging heap to mark freed heap memory
DEADBABE	"Dead babe", Used at the start of Silicon Graphics' IRIX arena files
DEADBEEF	"Dead beef", Famously used on IBM systems such as the RS/6000, also used in the original Mac OS operating systems, OPENSTEP Enterprise, and the Commodore Amiga. On Sun Microsystems' Solaris, marks freed kernel memory (KMEM_FREE_PATTERN)
DEADC0DE	"Dead code", Used as a marker in OpenWRT firmware to signify the beginning of the to-be created jffs2 file system at the end of the static firmware
DEADF00D	"Dead food", Used by Mungwall on the Commodore Amiga to mark allocated but uninitialized memory [13]
DEFEC8ED	"Defecated", Used for OpenSolaris core dumps
EBEBEBEB	From MicroQuill's SmartHeap
FADEDEAD	"Fade dead", Comes at the end to identify every AppleScript script
FDFDFDFD	Used by Microsoft's C++ debugging heap to mark "no man's land" guard bytes before and after allocated heap memory
FEE1DEAD	"Feel dead", Used by Linux reboot() syscall
FEEDFACE	"Feed face", Seen in PowerPC Mach-O binaries on Apple Inc.'s Mac OS X platform. On Sun Microsystems' Solaris, marks the red zone (KMEM_REDZONE_PATTERN)
FEEEFEEE	"Fee fee", Used by Microsoft's HeapFree() to mark freed heap memory

可能显示效果不是很好,请点击链接去WikiPedia查看:
Hexspeak Notable magic numbers
Magic_number_(programming) Magic_debug_values

===============华===丽===的===分===割===线===============
上海地铁结算系统
在人人网上看到一个同学上传的照片,看到我就笑了。
2^32 = 4294967296
奇葩的,是这位程序员使用unsigned int存储数据,然后除以100,得到想要的浮点数(两个零头):

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
int main(int argc, char **argv)
{
    char szBuffer[16];
    unsigned int nTmp = 0;
    sprintf(szBuffer, "0x%X", -20);
    nTmp = strtoul(szBuffer, NULL, 16);
    printf("%u\n", nTmp);
    return 0;
}

===============华===丽===的===分===割===线===============
展昭:“包大人,你额头的月牙是怎么弄的?”
包大人:“天生的。”
展昭:“揭得下来吗?”
包大人:“天生的。揭不下来。”
展昭:“如果揭下来呢?”
包大人:“揭下来……就是见证奇迹的时刻……”

另外,Google宣布Google Reader大限将至,不知道大家有没有好的替代品,以前用过鲜果,发现不支持本博客的代码高亮输出,另外网易有道阅读器也一样,代码乱得一团糟。最好是Web阅读器,到哪都可以看。


觉得文章还不错?点击此处对作者进行打赏!


本文地址: 程序人生 >> 调试器也有MagicNumer——从一个奇葩的错误说起
作者:代码疯子(Wins0n) 本站内容如无声明均属原创,转载请保留作者信息与原文链接,谢谢!


更多



  1. 2013年3月18日15:26 | #1

    第一个错误,一直以为把NULL赋给p就完事了。。第二个,那程序员果断自己要去坐公交啊 [em012] 。包大人那个笑死了。。。

    [回复]

    代码疯子 回复:

    @klion26, [em012] 就是同一块动态分配的内存被多个指针指向了,而其中应该赋NULL的没有赋值NULL

    [回复]

  2. 2013年3月23日09:10 | #2

    我博客域名改成the5fire.com了,方便的话修改下呗 [em022]

    [回复]

    代码疯子 回复:

    @the5fire, OK

    [回复]

  3. cuter44
    2013年3月23日09:30 | #3

    奇葩的,是这位程序员使用unsigned int存储数据,然后除以100,得到想要的浮点数(两个零头)
    ^ 这个…不是算定点数么.

    [回复]

    代码疯子 回复:

    @cuter44, 是想表达要换算成一个小数

    [回复]

    cuter44 回复:

    @代码疯子, 那个…意思我懂. 但是作为一个书呆子我坚定而肯定地指出:
    这个必须是定点数(点头

    [回复]

  4. 2013年3月23日15:36 | #4

    [em018] 我来踩踩你 [em012]

    [回复]

    代码疯子 回复:

    @zhxfl, 欢迎hx前来围观 [em022]

    [回复]

  5. 2013年3月31日13:01 | #5

    我的理解和你不同,不知道你理解的临时变量是什么含义:)。之所以会出现你说的错误,是因为你的代码用的递归删除,递归过程中p参数为你分配的节点指针,而非一直是指向root的指针。你释放的只是节点指针指向的内存,root中的节点指针地址是没有释放的。

    [回复]

    代码疯子 回复:

    @Eddy, root下的26个节点指针都会在递归函数中释放,可能你没看懂我的描述
    简单的说就是这样:
    int *p1 = new int();
    int *p2 = p1;
    delete p1;
    p1 = NULL;
    ================
    p2这个时候仍然只想释放后的内存,这时候p2 != NULL, p2 == 0xFEEEFEEE

    [回复]

    eddy 回复:

    @代码疯子, 你动态调试下你文中贴的这段代码看看有没有释放:)回复的这个例子和文中的我觉得不是一回事。

    [回复]

    Eddy 回复:

    是我的理解和你的有点小差别,你说的memset清零就好了

    [回复]

  6. jxbm
    2013年7月19日20:01 | #6

    楼主有几处不明,求指教

    关于第二次delete的问题,楼主说的对,光p=NULL是不行的。因为p只是传入参数的一个副本。另外,虽然 Delete(p->next[i]);了,但p->next[i]的内容仍为释放掉堆空间的地址,我的解决方法是
    Delete(p->next[i]);
    p->next[i]=NULL;
    貌似这样可以。

    另外,
    int *p1 = new int();
    int *p2 = p1;
    delete p1;
    p1 = NULL;
    之后,p2这个时候仍然指向释放后的内存,这时候p2 != NULL,p2仍为原值。
    *p2 == 0xFEEEFEEE,求核实。

    看了楼主的博客 受益匪浅 很好很强大 谢谢~

    [回复]

    代码疯子 回复:

    @jxbm, 你提到的第一点我认为也是可以的,每次处理完子节点之后,清空了对应的指针。至于第二点,你可能没理解我要阐明的主题。上面提到“堆管理器会给delete掉后的内存空间填充0xFEEEFEEE”,也就是你delete p1的时候,p1所指向的内存就会被填充为0xFEEEFEEE,接着p1 = NULL,只是把p1赋值为NULL,并不影响原来对应内存块中的0xFEEEFEEE,而你没有对p2操作,自然有*p2 == 0xFEEEFEEE。举个例子吧:

    int a = 10;
    int *p1 = &a;   // p1指向a
    int *p2 = p1;   // p2也指向a
    p1 = NULL;     // p1为NULL,并不影响p2执行a,那*p2的值就是10

    [回复]

  7. BLooM2
    2013年9月22日15:52 | #7

    void Delete(Node *p) 改为 void Delete(Node *& p) 就可以解决了

    [回复]

  8. segFault
    2014年8月23日02:30 | #8

    不知道怎么进来的 但是看了一半我就看出了bug 和你想的一样@BLooM2

    [回复]

  1. 本文目前尚无任何 trackbacks 和 pingbacks.