5

I know dereferencing a null pointer is undefined - but I would like to know what happens on a specific target - an MSP430.

I don't have a board to load this on in front of me to test this out right now.

What would happen if I did this (or similar)?

int * foo = NULL;
(*foo)++; //Crash?

Location 0x0 is in the SFR range and is reserved.

Would it generate a PUC/POR? Or would it silently "work"?

The assembly generated is

;int * foo = NULL;
clr.w   R15
;(*foo)++;
inc.w   R15

So location 0x0 is literally being incremented by 1.

When I run this in the simulator I see the value at address 0x0 go from 0 to 1. I get no warnings in the debug log and the program exits normally.

I am using the IAR EW430 compiler/assembler/simulator.

Nick
  • 1,361
  • 1
  • 14
  • 42
  • Just so its clear, you're asking for observed behavior of undefined behavior; a tall order. This question wouldn't be here if you had the actual *hardware* in your mits, is that right? And even then, wouldn't it be dependent on whether an overriding interrupt hook were installed at the time of the infraction, which would be dependent on the specific moment of said-same? (or do you have *complete* control over all of the above?). – WhozCraig Dec 28 '14 at 04:13
  • @WhozCraig that's correct. In a nut shell I am asking if the msp430 will violently shut down when 0x0 is read from or written to or does it perniciously keep working. I can't seem to figure out what that address would contain anyway. There is an interrupt you can setup that will fire when "vacant" memory outside of the normal range is accessed, from which you can do some logging about the event but I don't think that is an option here. More or less I am asking about a big unruly legacy code base that does not use asserts or otherwise check pointer parameters in interfaces. – Nick Dec 28 '14 at 04:54
  • address 0,1 are the interrupt enable bits. Therefore incrementing address 0 from value 0 to value 1 will enable one of the interrupts. The program will keep right on running. @Nick, have you not read the architecture sheet for the msp430? The architecture specification can be found at: – user3629249 Dec 28 '14 at 05:57
  • Since the behavior of dereferencing a null pointer is undefined (by the C standard), a C compiler won't *necessarily* generate code that accesses memory at address `0x0`, even if accessing that location is well defined by the CPU. (But apparently, with your code and whatever compiler and settings you're using, it happens to do so.) – Keith Thompson Dec 28 '14 at 10:37
  • @user3629249 No I had not seen that particular document before. I looked through the 5 series family user guide and saw "00000h-000FFh - Reserved for system extension" In the [memory map](http://www.ti.com/lit/ug/slau208n/slau208n.pdf#page=68). I should have done more homework though, because I am interested in this answer for the 5 series but also the 1 series. I am working with legacy code for both. – Nick Dec 29 '14 at 14:50
  • @KeithThompson Sure, I've seen instances where the compiler translated NULL into something totally different (like 0xFFFF), in assembly. This new address was something that would trigger a reset if illegally accessed... a bare metal segfault of sorts. I wish this target had that. – Nick Dec 31 '14 at 01:43
  • @Nick: Yes, but that's not the point I was making. Even if a null pointer is represented as `00000000h`, given `int *ptr = NULL; int deref = *ptr;`, a compiler needn't generate code that attempts to dereference address zero. It can simply replace it by, for example, a run-time trap instruction -- even if dereferencing address zero would not trap on the target processor. – Keith Thompson Dec 31 '14 at 01:55
  • @KeithThompson "run-time trap instruction"? This is a bit of a new idea for me. The C runtime I am working with is extremely minimal. I have no POSIX signals, like SIGTRAP. How would/could something like this be implemented on an embedded uC's runtime by a compiler vendor? If I am passing a pointer from one translation unit across to another translation unit and the address of that pointer is stuffed during runtime I don't think the compiler would ever be able to detect that a null pointer is going to be dereferenced at compile time. So what runtime checking could happen under the hood? – Nick Jan 02 '15 at 15:50
  • @Nick: The broader point is that undefined behavior is undefined. That means that a C compiler can do anything it likes for code whose behavior is undefined. Most C compilers don't generate code that does runtime checking (e.g., trapping on an out-of-bounds array access or defererencing of a null pointer), but the standard permits it. Compile-time checking for undefined behavior cannot be 100% reliable; there will always be cases that can't be detected. But when it can be detected, the compiler can diagnose it -- and it can, for example, generate code that *assumes* the behavior is defined. – Keith Thompson Jan 02 '15 at 18:34

1 Answers1

6

Not only writing and reading the address 0x0 will not cause a crash or a reboot, it actually a completely legal operation that is often used by MSP430 applications.

The initial portion or MSP430 memory map is reserved for I/O ports and control registers: http://en.wikipedia.org/wiki/TI_MSP430#MSP430_address_space

In particular, the control registers at 0x0 and subsequent addresses are:

 #define IE1_                  0x0000    /* Interrupt Enable 1 */
 #define IE2_                  0x0001    /* Interrupt Enable 2 */
 #define IFG1_                 0x0002    /* Interrupt Flag 1 */
 #define IFG2_                 0x0003    /* Interrupt Flag 2 */

So for example writing zero to that memory address by dereferencing a uint8_t * or uint16_t * pointer is going to disable interrupts. Writing zero by dereferencing an uint32_t * it is also going to clear the flags. Incrementing the value of these registers does not make a lot of sense, but should be completely legal.

At least this is the case on msp430 Series 1, Series 2 and Series 4. By checking the header files I was not able to find anything mapped to 0x0 on Series 5 (the interrupt control registers are mapped to region starting from 0x0100).

So if you want to catch places in code where the NULL pointer is dereferenced, you're completely on your own.

kfx
  • 8,136
  • 3
  • 28
  • 52
  • Thanks for the explanation, that's what I was afraid of. I was looking at a series 5 header which is why I could not figure out what 0x0 was used for. – Nick Dec 28 '14 at 14:43