5 ms·
Just for the hardware-minded: dereferencing null is perfectly fine on Cortex-M processors and is well-defined. Yes, that's hardware, but a nice place where har
by an_d_rew 11y ago
Just for the hardware-minded: dereferencing null is perfectly fine on Cortex-M processors and is well-defined.
Yes, that's hardware, but a nice place where hardware ain't C.
I'm just sayin'...
- qb45 11y agoDo you mean some quirk unique to these CPUs or do they simply have RAM mapped at this address? The latter is nothing unusual, x86 has it as well.
- CountSessine 11y agoLike many embedded cores, Cortex-M's don't have an MMU - everyone sees physical memory. In fact, I believe that's the chief distinguishing feature of the 3 core levels - Cortex-M's have no MMU, Cortex-A's have memory paging, and I think Cortex-R's are in between and have memory segments like MIPS. So if the board designer wired everything up to map RAM at address 0, then yes, you'd be able to dereference a pointer to address 0. Completely beside the point, though. Dereferencing NULL is always undefined behaviour in C.
- amluto 11y agoI'm a bit curious how a pointer with value 0 ended up being banned by C. Specifically, on x86 real mode (which includes modern systems early in boot before switching to protected mode), there's a hardware defined data structure at address zero: the "Interrupt Vector Table". Maybe everyone who wrote real-mode systems software either ignored that bit of the C standard, or they fudged around it (IIRC the IVT is an array and the first entry doesn't matter, so you can start a few bytes past zero), or they used assembly to access it.
- qb45 11y agoIt seems that 0 wasn't a usable address on PDP-11 so K&R decided it's fine to use it as null pointer constant. And of course dereferencing 0 isn't impossible in practice on arches which support this. Compilers for such machines usually can be coerced to generate code which accesses 0, you just have to live with the fact that your code isn't considered "valid, portable C" anymore.
- tzs 11y agoIn C, these two code fragments do not necessarily give the same result (assume pointers and longs are the same size): long zero = 0; struct IVT * ivt_ptr = (struct IVT *)zero; and struct IVT * ivt_ptr = 0; /* or (struct IVT *)0 if you like that better stylistically */ The former gives you a pointer whose bits are all 0. The latter gives you a null pointer. On systems where 0 is a valid memory address the compiler should pick some other address that is not valid. Similarly, these two are not necessarily the same: if ( ivt_ptr == 0 ) ... and if ( ivt_ptr == (struct IVT *)zero ) ... /* or if ((long)ivt_ptr == zero ) ... */ 0 only represents the null pointer in C when it is the constant 0. See this for more information: http://c-faq.com/null/ http://c-faq.com/null/
- qb45 11y ago> On systems where 0 is a valid memory address the compiler should pick some other address that is not valid. Not necessarily. Null dereference is undefined behavior so it may as well access some valid memory. Even on platforms where 0 is a valid address, practical compilers tend to use 0 as null constant because it simplifies implementation (easy to check for null, matches the C syntax without WTFs). This comes at the cost of making some small part of memory unusable to portable C code (nobody will believe you when you return 0 from malloc if 0 happens to be considered null), but that's fine because such memory is typically used for machine-specific, nonportable stuff anyway.
- masklinn 11y ago> dereferencing null is perfectly fine on Cortex-M processors and is well-defined It's also perfectly fine on x86, in kernel memory page 0 is often mapped, the OS is the one mapping page 0 so that userland dereferencing it faults.
- qb45 11y agoWhich x86 kernel maps something at 0? In Windows and Linux, the low part of virtual address space is reserved for userspace. In fact, Linux had several privilege escalation bugs which involved putting something at 0 and executing a buggy syscall which loaded this thing due to NULL dereference and believed it's some legit internal kernel data.
- 0x0 11y agoWhich was probably the reason why "mmap_min_addr" was added :) https://wiki.debian.org/mmap_min_addr https://wiki.debian.org/mmap_min_addr
- makomk 11y agoMappings at zero are used by legacy code like 16-bit Windows applications, if I remember correctly.
- jevinskie 11y agoOS X and iOS map in __TEXT,__PAGEZERO to 0x0.* Of course, it is mapped without any protection levels so it isn't of much use except for catching most nullptr dereferences (just 4 GB of protection on x86_64!). * Well, this is implemented by the default system toolchain. You'r particular toolchain can opt out of this behavior if they want to.
- noselasd 11y ago"perfectly fine" does not depend on the processor alone, it depends on the compiler. A compiler for x86 can assume dereferencing a NULL pointer is undefined behavior, and optimize your code in ways you perhaps didn't expect. Or it could document and provide a well defined behavior of derefrencing a NULL pointer. (e.g. gcc provides -fdelete-null-pointer-checks to control this)
- klodolph 11y agoDereferencing NULL, in a C program, is ALWAYS undefined behavior. The fact that you are using a Cortex-M processor does not change this fact. It is spelled out very clearly in the C standard.
- masklinn 11y ago> Dereferencing NULL, in a C program, is ALWAYS undefined behavior. 1. you're not required to use C 2. an extension to the C standard can decide to define that behaviour
- cygx 11y agoAlso note that null pointers may not need to be represented by the all-zero bit pattern, ie the conversion (void*)(intptr_t)0 can in principle be more involved than a noop.
- silon7 11y agoI've used at times it to crash the program (core dump), producing a cleaner stack than abort().
- astrange 11y agoYou should use __builtin_trap(). Invoking undefined behavior lets the compiler justifiably delete the entire code path leading up to it.
- _kst_ 11y ago"Undefined behavior" means behavior that is not defined by the C standard. Things other than the C standard (secondary standards like POSIX, compiler documentation, hardware specifications, etc.) are free to define the behavior of any construct that's not defined by the language standard. And an optimizing compiler is free to transform its generated code based on the assumption that the code's behavior is defined.
- cygx 11y agoArguably, undefined behaviour is more like 0/0 in (ordinary) mathematics than just something the C standard fails to specify (you want implementation defined for that one). An optimizing compiler is free to transform its generated code based on the assumption that undefined behaviour never occurs.
- Gibbon1 11y agoThere is of course the issue whether you want the compiler to do that. Usually optimizing one parameter in a design to the exclusion of everything else is an strong indicator that the engineers involved are functionally incompetent.
- pekk 11y agoYou're designing a standard for people to implement in their specific compilers, and you want to let them generate fast code... I hardly think the mere existence of "undefined behavior" indicates incompetence.
- riwsky 11y agoPardon the bald appeal to authority, but your assertion reminds me of a Richard Hamming quote: """ As we say, the volume is almost all on the surface. Even in 3 dimensions the unit sphere has 7/8-ths of its volume within 1/2 of the surface. In n-dimensions there is 1–(1/2^n) within 1/2 of the radius from the surface. This has importance in design; it means almost surely the optimal design will be on the surface and will not be inside as you might think from taking the calculus and doing optimizations in that course. The calculus methods are usually inappropriate for finding the optimum in high dimensional spaces. This is not strange at all; generally speaking the best design is pushing one or more of the parameters to their extreme—obviously you are on the surface of the feasible region of design! """
- Gibbon1 11y agoIf I do a read of a 'null' pointer on the Cortex ARM I'm using it's the initial stack pointer. If you try to write to that without some special magic involving the flash controller one gets a bus fault interrupt. Behavior depends on what the ISR does. AVR Processors don't have bus faults. Some machines I've worked on have RAM based at address zero.