44

For C++ development for 32-bit systems (be it Linux, Mac OS or Windows, PowerPC or x86) I have initialised pointers that would otherwise be undefined (e.g. they can not immediately get a proper value) like so:

int *pInt = reinterpret_cast<int *>(0xDEADBEEF);

(To save typing and being DRY the right-hand side would normally be in a constant, e.g. BAD_PTR.)

If pInt is dereferenced before it gets a proper value then it will crash immediately on most systems (instead of crashing much later when some memory is overwritten or going into a very long loop).

Of course the behavior is dependent on the underlying hardware (getting a 4 byte integer from the odd address 0xDEADBEEF from a user process may be perfectly valid), but the crashing has been 100% reliable for all the systems I have developed for so far (Mac OS 68xxx, Mac OS PowerPC, Linux Redhat Pentium, Windows GUI Pentium, Windows console Pentium). For instance on PowerPC it is illegal (bus fault) to fetch a 4 byte integer from an odd address.

What is a good value for this on 64-bit systems?

Zain Shaikh
  • 6,013
  • 6
  • 41
  • 66
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 6
    I've seen systems where the first 1K of memory is defined to be not valid. So if a NULL pointer is dereferenced, the process will die a quick death. 0xDEADBEEF could be a valid location. – Robert Deml Aug 11 '09 at 01:47
  • 3
    @Robert: I've seen systems where the interrupt vector starts at 0, so dereferencing a NULL function pointer just seems to reboot the system (but doesn't reinitialize the stacks, etc.). Any address could be a valid location for something. – bk1e Aug 11 '09 at 04:55
  • 11
    @bk1e: IVT should **NEVER** be accessible from usermode. But you are correct in that there's no reason why address `0` can't be mapped. In Linux, it's easy to map to address `0` by changing an option in the kernel. In any case, the lesson learned here is not to use stupid patterns to mark pointers as invalid, use `null` or a separate flag in the struct. Assuming it will just crash is completely irresponsible and ignorant, if you're lucky, you will only get a segfault, it's likely that this can lead to remote code execution, and already has many, many, many, times in the past. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ Aug 02 '10 at 20:11
  • 1
    This is why C should be banned. Nobody knows how to use it. On another note, you could just ensure that your program maps 0xdeadbeef to a guard page before running, for example, but not all OS have an equivalent, so you may still get the vuln on some OS. Also triggering a segfault itself doesn't necessarily stop remote code execution from happening. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ Aug 02 '10 at 20:11
  • @Longpoke: I agree, but not every CPU has an MMU. On some embedded systems, NULL points at something important and you can't do anything about it. :) – bk1e Aug 03 '10 at 04:59
  • 3
    @bk1e, but the C standard guarantees that NULL (and respectively 0 converted to a pointer) will be an invalid pointer which you can't dereference. – CMircea Apr 26 '11 at 21:12
  • 3
    @iconiK: What does "can't dereference" mean? You most certainly can dereference NULL, but the result is undefined behavior (according to C99 section 6.5.3.2). A null pointer is guaranteed to compare unequal to any object or function (according to C99 section 6.3.2.3), so if there is a C object or C function at address 0, the compiler ought to convert `(void*)0` to point somewhere other than address 0. However, the interrupt vector table isn't a C object or C function, so I don't think compilers are obligated to guarantee that NULL doesn't point to the interrupt vector table. – bk1e Apr 27 '11 at 14:56
  • 2
    @bk1, well unless you explicitly want undefined behavior, you ought not to dereference NULL, so that is why it's the proper one to use for initialized but unused pointers. I didn't say it was address 0; the C standard guarantees that 0 converted to a pointer results in the NULL pointer. Whether an implementation changes that behavior, or defines dereferencing the NULL point is outside the scope of standard C (or C++ for that matter). – CMircea Apr 27 '11 at 17:44
  • 2
    @iconiK: Back to the original topic: You can accidentally dereference 0xDEADBEEF, which may or may not cause a crash. You can accidentally dereference NULL, which may or may not be address 0, and which may or may not cause a crash. Many platforms use memory protection hardware to detect when an invalid pointer is dereferenced, but the C specification does not require this. Therefore, initializing pointers to NULL is better than initializing them to 0xDEADBEEF, but it is not guaranteed to detect accidental dereferences on all platforms. – bk1e May 01 '11 at 21:42

12 Answers12

72

0xBADC0FFEE0DDF00D

chaos
  • 122,029
  • 33
  • 303
  • 309
  • 5
    This particular one has the advantage that you need yottabytes of memory to actually make this a sane value as kernel heap grows down while user heap grows up. – Joshua Aug 02 '10 at 16:14
  • 9
    @Joshua: 1. I can map my pages wherever I like. 2. That's a very dangerous assumption to make (perhaps its true with your current os on an x86 setup, but this is implementation dependent), what if the stack starts at 0xBADCOFFEE0DFOOD-0x1000? The stack starts at random places already, it does this to try and make exploitation harder and for just technical reasons aside from that. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ Aug 02 '10 at 20:17
  • 1
    How did you arrive at this number? – David G Apr 29 '13 at 20:56
55

According to Wikipedia, BADC0FFEE0DDF00D is used on IBM RS/6000 64-bit systems to indicate uninitialized CPU registers.

Thomas Owens
  • 114,398
  • 98
  • 311
  • 431
27

Most current 64-bit systems let you use only the lowest 248–252 bits of the address space; higher bits of the address must be all-zero. Some chips (e.g. amd64) also let you use the highest 248–252. Addresses outside these ranges cannot ever be mapped to accessible memory; the hardware simply won't allow it.

I therefore recommend you use a value close to 263, which is nowhere near either of the possibly-usable spaces. If the leading four hex digits are 7ff8, the value will be a double precision floating-point NaN, which is convenient. So my suggested cute hexadecimal phrase is 0x7FF8BADFBADFBADF.

By the way, you really don't want to use a value close to 0, because that makes it hard to tell an offset dereference of NULL — a structure member access, for instance — from a dereference of the poison pattern.

zwol
  • 135,547
  • 38
  • 252
  • 361
  • 1
    That «most current 64-bit system let you use only the lowest 48-52 bits of the address space» and that «the hardware simply won't allow it». – nalply Dec 19 '10 at 10:23
  • 3
    The architecture manual for each CPU of interest will tell you how many bits of the address space are usable. For instance, the [AMD64 Architecture Programmer's Manual, Volume 2 (System Programming)](http://support.amd.com/us/Processor_TechDocs/24593.pdf) says in section 1.1.3 that not all 64 bits of the address space are necessarily usable and that unusable high bits must be all-zero or all-one, and then in section 5.3.3 shows you that current implementations only allow use of the low 48 bits. – zwol Dec 19 '10 at 17:02
  • 1
    I'm not aware of any *overview* that lists CPUs with 64-bit addressing and their usable address ranges, but the only *exception* to my original assertion that I can find is PowerPC in hashed page table mode. With tree-structured page tables, every additional six to eight bits of virtual address space adds a tree level and makes TLB misses slower, so architecture designers are reluctant to make more space usable than people actually need, which right now is 48-52 bits (except for exotic [single address space operating systems](http://c2.com/cgi/wiki?SingleAddressSpaceOperatingSystem)). – zwol Dec 19 '10 at 17:08
25

Generally it doesn't matter exactly what pattern you write, it matters that you can identify the pattern in order to determine where problems are occurring. It just so happens that in the Linux kernel these are often chosen so that they can be trapped if the addresses are dereferenced.

Have a look in the Linux kernel at include/linux/poison.h. This file contains different poison values for many different kernel subsystems. There is no one poison value that is appropriate.

Also, you might check per-architecture include files in the Linux kernel source tree for info on what is used on specific architectures.

nkr
  • 3,026
  • 7
  • 31
  • 39
Noah Watkins
  • 5,446
  • 3
  • 34
  • 47
15

I'm assuming you've already discounted NULL (i.e. 0 without the typecast). It's definitely the safest choice, as, in theory, a valid pointer could point to the memory address 0xDEADBEEF (Or any other non-NULL memory address).

Michael Koval
  • 8,207
  • 5
  • 42
  • 53
  • I think one of the points of using 0xDEADBEEF or some other known-bad value is that it's quite obvious when debugging the resultant core file (ore equivalent) that you're dealing with an uninitialised pointer, rather than garbage/invalid/incorrect data. I'd suggest the chances of 0xDEADBEEF actually pointing to a valid memory location are small enough to be greatly outweighed by the benefits. – Rodyland Aug 11 '09 at 04:29
  • 6
    For Pointers, Always NULL/0 should be used. 0xDEADBEEF and other hex-values are used to fill recently-allocated-but-not-initialized , recently-freed-should-not-be-accessed-any-more memory regions handled by the heap-manager functions (this may also be true for the stack)... – Malkocoglu Aug 11 '09 at 06:52
  • 1
    What you've never seen a memory mapping at NULL? – Joshua Aug 17 '09 at 20:58
  • 2
    No one has ever seen a memory mapping at NULL as the compiler guarantees that the NULL memory location is never used: "...a null pointer points definitively nowhere; it is not the address of any object or function." See: http://c-faq.com/null/null1.html – Michael Koval Aug 20 '09 at 16:46
  • 2
    @Mike Koval, well that's just simply not true. I've mapped memory at NULL. In fact it's not that uncommon, a quick Googling of "null pointer dereference exploit" should give you lots of examples. This looks like it probably has some high-level information: http://blog.cr0.org/2009/06/bypassing-linux-null-pointer.html – mrduclaw Oct 14 '09 at 13:41
  • 2
    @Mike: That is true, however the C specification does not define the behavior of an attempt to dereference a NULL pointer. Thus it is legal (though atypical) for a platform to accept reads from and writes to *NULL. – ephemient Jan 18 '10 at 18:04
  • 3
    The NULL/0 pointer is not necessarily represented as 0-bytes in memory. Although I wouldn't know of any system where it is anything different. – Thomas Aug 02 '10 at 20:54
  • 1
    @mrduclaw So qualify it a little - as long as you stay in userland, you're guaranteed that NULL is absolutely invalid. – Jakob Borg Aug 02 '10 at 21:00
  • 1
    Just so you know, mmap() will map NULL if explicitly told to do so (passed flag for exact address with NULL as the address). and in gcc compiling with -fno-delete-null-pointer-checks tells the compiler not to assume that dereferencing NULL is invalid. – Joshua Aug 03 '10 at 03:13
13

0xDEADBEEFBAADF00D might work.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Joshua
  • 8,112
  • 3
  • 35
  • 40
8

I don't have a good choice for you, but here's a list of hex words that you can use to make your phrase.

Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
5

Two 0xDEADBEEFs should be enough, I think..

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
LostMohican
  • 3,082
  • 7
  • 29
  • 38
3

I see several answers claiming NULL is a good choice, but I disagree.

NULL is often used as a valid return value from functions. It indicates a failure return or an unknown value. This is a different meaning than "uninitialized pointer."

Using a debugger on the code and seeing NULL would then leave two possibilities: the pointer was never initialized or it had failed a memory allocation.

Setting the uninitialized pointer to 0xDEADBEEF or the 64-bit equivalent means that a NULL pointer indicates an intentional value.

Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
  • This is only true if you need an uninitialized pointer. And that's where I disagree: Unless you do embedded stuff or other things that are so performance-sensitive, that initializing a pointer to `NULL` is not possible, in the _very_ rare cases where you need a pointer without being able to initialize it to something sensible, initializing it to `NULL` gets you rid of the problem. I don't think I needed an uninitialized pointer in years. – sbi Aug 11 '09 at 09:05
  • 2
    "That was never initialized" values are very useful when debugging - one doesn't *explicitly* initialize any given memory location this way, one fills entire memory blocks with it (on malloc() and again, with a different pattern, on free()) so as to detect places where the program *should have* initialized a variable but didn't. – zwol Aug 02 '10 at 20:34
2

It depends on the OS and the environment, of course. I don't think 0xDEADBEEF is necessarily a bad pointer in an arbitrary 32-bit system, either.

Realistically, any modern OS should be access-protecting the first few pages of process memory, so NULL should be a good invalid pointer value. Conveniently enough, it's already pre-defined for you.

Mark Bessey
  • 19,598
  • 4
  • 47
  • 69
  • 2
    Agree that NULL is a very useful choice. 0xDEADBEEF isn't an invalid pointer, but not only is it awfully unlikely in practice, but it's not aligned on a 4- or 2-byte boundary. Except as a char* or possibly bool*, it won't occur naturally unless you've got some seriously messed-up memory semantics. – ChrisV Aug 11 '09 at 01:50
  • 2
    It's almost always in kernel memory mapping area anyway. – Joshua Aug 17 '09 at 20:59
2

As the system I worked on basically runs on x86_64 platform, the value I use is:

0xDEADBEEFDEADBEEF

Reasons are:

  • On x86_64 platform, only the low-order 48 bits are used for virtual memory address in current implementation, meaning any value > 2^48 should work: https://en.wikipedia.org/wiki/X86-64
  • As 0xDEADBEEF is already very well known for this purpose in 32bit, 0xDEADBEEFDEADBEEF in 64bit is just more 'backward compatible'
Baiyan Huang
  • 6,463
  • 8
  • 45
  • 72
1

0x42 could work on both 32bit and 64bit ? (It should still trigger a crash since it is close enough to the NULL pointer, and given that it's rather large, chances are you would not have it within a regular dereference of a structure field with the structure pointer being NULL).

chaos
  • 122,029
  • 33
  • 303
  • 309
Andrew Y
  • 5,107
  • 2
  • 28
  • 29