Search code examples
cvolatile

How can the volatile keyword affect a static const array?


This is a mindblower and anyone who can answer it deserves massive recognition! It is actually a couple of connected questions that I am asking to get better understanding.

The drivers for the STM32 ARM Cortex platform have the following code in them:

static __I uint8_t APBAHBPrescTable[16] = {0, 0, 0, 0, 1, 2, 3, 4, 1, 2, 3, 4, 6, 7, 8, 9};

__I is defined as:

#ifdef __cplusplus
  #define     __I     volatile                /*!< defines 'read only'   permissions      */
#else
  #define     __I     volatile const          /*!< defines 'read only'   permissions      */
#endif

My program is a C program compiled with a GCC cross-compiler. Thus the array declaration is effectively:

static volatile const uint8_t APBAHBPrescTable[16] = {0, 0, 0, 0, 1, 2, 3, 4, 1, 2, 3, 4, 6, 7, 8, 9};

Question 1:

Given that this is a constant array, why use the volatile keywork here?

My understanding is that the volatile keyword means that the contents of the array can change, but the const means that they cannot.

The only use of this array in the code is three uses like this:

tmp = RCC->CFGR & CFGR_PPRE1_Set_Mask;
tmp = tmp >> 8;
presc = APBAHBPrescTable[tmp];

When I dump the values of tmp and presc I find that tmp has a value of 4 and presc has a value of 0. Index 4 is the 5th element of the array which has a value of 1. There are no other accesses or uses of this value...At all...Anywhere.

Question 2:

How might the value changed between it being declared?

When I dump the array I see it is filled with zeroes.

It happens reliably...until I remove the __I from the array declaration. This makes me think it is not a buffer overflow. Other than that I cannot think of anything.

I would think that the volatile keyword was there for a reason, except that I also saw code like the following in an interrupt handler where, as far as I understand, the volatile keyword is redundant:

volatile uint32_t status = USART2->SR;

This variable is local to the function and as such can never be changed by code elsewhere.

======== EXTRA DETAIL ========

Here is the annotated disassembly of the relevant piece of code. The value at (RCC_GetClocksFreq+128) is zero, but appears at some point to have had the address of the prescaler lookup table copied into it:

0x000001d0 <+56>:    ldr     r1, [pc, #68]   ; (0x218 <RCC_GetClocksFreq+128>)
       ...
   tmp = RCC->CFGR & CFGR_PPRE1_Set_Mask;
   tmp = tmp >> 8;
0x000001de <+70>:    ldr     r4, [r2, #4]
0x000001e0 <+72>:    ubfx    r4, r4, #8, #3
   presc = APBAHBPrescTable[tmp];
0x000001e4 <+76>:    ldrb    r4, [r1, r4]
   RCC_Clocks->PCLK1_Frequency = RCC_Clocks->HCLK_Frequency >> presc;
0x000001e6 <+78>:    lsr.w   r4, r3, r4
0x000001ea <+82>:    str     r4, [r0, #8]

Here is the same, but with the volatile const macro replaced with const:

0x000001d0 <+56>:    ldr     r4, [pc, #68]   ; (0x218 <RCC_GetClocksFreq+128>)
       ...
   tmp = RCC->CFGR & CFGR_PPRE1_Set_Mask;
   tmp = tmp >> 8;
0x000001de <+70>:    ldr     r1, [r2, #4]
0x000001e0 <+72>:    ubfx    r1, r1, #8, #3
   presc = APBAHBPrescTable[tmp];
0x000001e4 <+76>:    ldrb    r1, [r4, r1]
   RCC_Clocks->PCLK1_Frequency = RCC_Clocks->HCLK_Frequency >> presc;
0x000001e6 <+78>:    lsr.w   r1, r3, r1
0x000001ea <+82>:    str     r1, [r0, #8]

They are essentially identical. Yet somehow removing the volatile keyword solves the problem!


Solution

  • First, thanks for all the comments and answers that led me to this answer.

    When the variable is defined without the "volatile" keyword it is put into a readonly section of the binary file.

    When the variable is defined with the "volatile" keyword it is put in the same section of the binary file as all other variables.

    I have recently found 3 buffer overruns and I am sure there are others. A lot of the code is not very well written. It is likely that when the "volatile" keyword is specified the variable is so placed in memory as to make it vulnerable to a buffer overrun. There is no reason at all for this particular variable to be marked as volatile so the simple fix is to remove that keyword. The proper fix is to do that and also track down the buffer overrun and fix it.