1

I have an application written for an STM32-L552ZE Cortex M-33 based MCU which makes use of TrustZone-M.

Both worlds have a main.h, main.c and both wolds' header files declare a variable foo (i.e. both the secure main.h and nonsecure main.h declare foo).

To my mind, this should be fine because the way I learned about TrustZone (admittedly, I am coming more from a TrustZone-A, SGX world) makes me believe that the two worlds are independent from each other and only interact through a welldefined interface (secure-/nonsecure function calls through the NSC region).

Unfortunately, when debugging my code, I noticed that the nonsecure world's writes to foo were ignored. Through checking the address of foo, which was at 0x3..., I noticed that it was allocated in the secure world's SRAM. After having uncommented the secure world's foo declaration and leaving the rest unchanged, the memory address of foo seen by the nonsecure world changed to 0x2..., which is in the nonsecure world.

Question:

How is a variable in the secure world able to shadow a nonsecure variable? I am aware that TrustZone-M implements separation through memory addresses, though I would have assumed the linker to be smart enough to differentiate between the two instances of foo.


Clarification:

To make the situation clearer, following is an excerpt from the code:

secure main.h:

#ifndef __MAIN_H
#define __MAIN_H

/* Includes */
#include "stm32l5xx_hal.h"
#include "secure_nsc.h"        // Header file declaring the functions into secure world

/* Private includes*/
/* USER CODE BEGIN Includes */

#include "inttypes.h"
#include "string.h"

/* USER CODE END Includes */

...

typedef struct ringbuf{
    volatile int16_t prod_ind;
    volatile int16_t cons_ind;
    volatile int16_t isr_ctr;
    volatile bool_t  empty;
             byte_t  buf[RBUF_LEN];
}ringbuf_t;

ringbuf_t foo;

...

#include "helper.h"

#endif /* __MAIN_H */

nonsecure main.h:

#ifndef __MAIN_H
#define __MAIN_H
/* Includes */
#include "stm32l5xx_hal.h"

#include "secure_nsc.h" /* For export Non-secure callable APIs */

/* Private includes */
/* USER CODE BEGIN Includes */
#include "inttypes.h"
#include "string.h"
/* USER CODE END Includes */

...

typedef struct ringbuf{
    volatile int16_t prod_ind;
    volatile int16_t cons_ind;
    volatile int16_t isr_ctr;                                                                           
    volatile bool_t  empty;
             byte_t  buf[RBUF_LEN];
}ringbuf_t;

ringbuf_t foo;

...

#include "helper.h"

#endif /* __MAIN_H */

Both helper.h, i.e. the secure and nonsecure one, have the following declaration (the definition exists in their respective helper.c files):

bool_t rb_produce_one(ringbuf_t* rb, byte_t ibyte);

This function rb_produce_one is the one used by the nonsecure world to access its ringbuffer foo.

Secure main.c doesn't use its instance of foo (yet, will use it later).

Nonsecure main.c accesses foo through a function which is declared in helper.h and defined in helper.c (again, helper.h, helper.c exist twice, once in the secure and once in the nonsecure world).

iMrFelix
  • 335
  • 2
  • 18
  • How are these variables defined? Are they external linkage global variables with tentative definitions in header file? (That would be bad even within single application) – user694733 Jun 28 '22 at 13:16
  • @user694733 I expanded the question to include more details. The variables ```foo``` are declared in their respective main.h. The secure world's main.c does _not_ yet use its ```foo``` at all, but the nonsecure world initializes ```foo``` in its nonsecure ```main.c``` and subsequently operates on ```foo``` through a function ```rb_produce_one``` declared in the nonsecure ```helper.h``` and defined in the nonsecure ```helper.c```. Nothing is explicitly declared as external (nor as static). – iMrFelix Jun 28 '22 at 14:37
  • I don't know exactly how your compiler treats TZ linking, but TZ or not, that tentative variable definition (= no linkage specifier and initializer) is definitely wrong. 1. There is no reason to have global variable (= external linkage). Use `static`. 2. *Define* variables in source files, not in headers. Only *declarations* should go to header, but since there is no need to have global non-const variables, you only need *definitions* in source files. 3. If for some reason you ignore advice 1 (please don't), always initialize globals to avoid tentative definition. – user694733 Jun 29 '22 at 08:50
  • Sidenote: Identifiers that begin with double underscores (like `__MAIN_H`) are reserved. Easiest way to avoid problems with reserved symbols, is to not use underscore `_` to start identifier. – user694733 Jun 29 '22 at 08:58
  • Good point about the identifiers, though I didn't add them myself, my IDE (STM32CubeIDE) adds them by default – iMrFelix Jun 29 '22 at 09:09
  • About your three points: (1) Agree in principle, though for TrustZone when you call from the nonsecure world into the secure world I don't see how else I could keep state that is accessible to several secure world functions residing in different source files besides having global variables. (2) I'm not aware of any definitions in the header files, ```ringbuf_t foo;``` is just a declaration? (3) I initialized all global variables before use at the top of the main function, though as I hadn't used ```foo``` in the secure world yet, I didn't feel the need to initialize it yet. – iMrFelix Jun 29 '22 at 09:21
  • (1) I haven't used much TZ, but as far as I know, I don't think it differs from regular application with multiple modules: Modules only share data between each other through function calls and their arguments. Without knowing exact details of your application, I would expect module which owns ringbuffer to offer functions to others to either read or write, rather than giving everybody direct access to it. – user694733 Jun 29 '22 at 09:38
  • (2) `ringbuf_t foo;` is **tentative** definition, because it is in file scope and missing linkage specifier and initialization. It's obsolete C feature that should be avoided always. – user694733 Jun 29 '22 at 09:38
  • (3) Try to initialize both secure and non-secure `foo`. I would expect linker error. But like I said, I am not sure how your compiler handles TZ linking, so perhaps there is no error? – user694733 Jun 29 '22 at 09:44

0 Answers0