Tokens Improve IoT Security

By Ralph Moore · October 15, 2026 · SecureSMX

Introduction

Even though malware may be sand-boxed in an isolated partition, it can still attack system objects. It would seem that isolated partitioning would prevent this. However, if a hacker can determine a valid RTOS object address, such as that of a semaphore, and wrap it in a local handle, it can then signal the semaphore as follows:

   SCB_PTR proxy_hdl;
   *proxy_hdl = 0x2000ab12       /* sem_addr_guess */
   SemSignal(proxy_hdl);

Here, proxy_hdl is in a data region in the MPU. sem_addr_guess is an address outside of the MPU – i.e. it is in another partition. When SemSignal() is executed, proxy_hdl passes the MPU region test and its contents through to SemSignal(). If the hacker guessed right and sem_addr_guess is a valid semaphore handle, we have a problem.

In addition, malware in a breached partition could bring down an IoT device by using up system resources. For example, it could keep creating semaphores until the system ran out of semaphore control blocks. Then another task needing a semaphore would not be able to get it and thus would fail to do its job. Allocating all resources during initialization is a possible solution to avoid resource exhaustion, but it may not always be practical. That is because tasks often share resources and memory, while running. Allocating all resources, in advance, may use too much memory or it may be difficult to determine what each task needs.

Isolated partitioning is not protecting against either of these attack vectors, so clearly, another means of protection is needed.

Tokens To The Rescue

When an object is created, such as a semaphore:

   SCB_PTR  semA;
   semA = SemCreate(EVENT, 1, “semA”, &semA);

semA is the handle of semaphore A. It is a memory location that contains the address of  the semaphore control block (SCB) for semA. Handles are global objects. They are defined at compile time and assigned addresses at link time. Object control blocks are allocated and initialized, at run time, from block pools by object create services, such as SemCreate().

A token is the address of the handle for an object. In the function shown above, &semA is the token for semA. It is passed in as a parameter and stored in the semA SCB. Thus an object knows its handle. Tokens are intended for use from unprivileged mode (umode) tasks.

There are two types of tokens: a HI token allows creating, deleting, modifying, and accessing an object, and a LO token permits accessing an object (e.g. to test or signal a semaphore). Since a handle must be word-aligned, bits 0 and 1 are not needed. So bit 0 = 1 for HI and = 0 for LO.

A token list is compiled for each task that requires tokens as follows:

   u32 ut2a_tl[] = {(u32)&semA, (u32)&semB, 0};    /* ut2a token list */
   u32 ut2b_tl[] = {(u32)&semA+HI, (u32)&semC, 0}; /* ut2b token list */

Task ut2a is allowed to access semA and semB, but it is not allowed to create nor modify either one. Task ut2b is allowed to create and modify semA and to access semC. 0 marks the end of each list.

A token list is assigned to a task after the task is created, as follows:

   ut2a = TaskCreate(ut2a_main, PRI2, 500, UTASK, “ut2a");
   TaskSet(ut2a, SET_TL, (u32)&ut2a_tl);
   ut2b = TaskCreate(ut2b_main, PRI2, 500, UTASK, "ut2b");
   TaskSet(ut2b, SET_TL, (u32)&ut2b_tl);

This is done in pmode. Token lists are stored with control blocks in pmode and are not accessible from umode.

If no token list is assigned to a task, the task does not require tokens to access and modify objects. No tokens required is appropriate for mission-critical tasks and for system tasks such as those for initialization and recovery. As with runtime limits [Ref 1], we do not want to lose a battle for lack of a token or sufficient runtime.

Tokens In Action

In the following, task ut2a attempts to create semA, but it fails because it has only a LO token for semA (see above):

   bool pass;

   void tsem_ut2a(u32)
   {
      semA = SemCreate(SEM_EVENT, 1, "semA", &semA);
      if (semA == NULL)
         pass = false;
   }

results in a TOKEN_VIOL error and SemCreate() fails.

Task ut2b successfully creates semA because it has a HI token for semA, but it cannot create semA twice.

   void tsem_ut2b(u32 mode)
   {
      semA = SemCreate(SEM_EVENT, 1, "semA", &semA);
      if (semA != NULL)
         pass = true;
      semA = SemCreate(SEM_EVENT, 1, "semA", &semA);
      if (errno == INV_OP)
         pass = false;
   }

The second SemCreate() returns an INV_OP error, because semA must be deleted before it can be created again. Thus a hacker cannot create an object repetitively in order to use up all control blocks for that object. Nor can a hacker define fake semaphore handles then create semaphores for them because his breached task would not have tokens for those handles.

As shown in the Introduction, another possible attack vector is to guess a semaphore handle and then use it to cause trouble. But to do this, the breached task must have a token for that semaphore. A hacker cannot assign a token from inside of a breached umode partition, because assigning tokens to tasks can be done only in pmode.

How Proxy Handles Work

As shown, proxy handles provide attack vectors for bad actors. Why would we create such an attack vector? The reason is that it is sometimes necessary for a task in one partition to be able to perform an action upon an object in another partition, for example, to signal a semaphore. The reason that an object created in one partition cannot be directly accessed from another partition is shown in the assembly code as follows:

      LDR R0, ??DataTable25_1            ; semA handle address
      LDR R0, [R0]                       ; -> MMF
      BL SemTest
   ??DataTable_25:
      DC32 semA

The compiler wants to pass the address of semA’s SCB, not the address of its handle. However, if semA was created in pmode or in another umode partition, its handle is outside of the MPU, so the second LDR causes a memory manage fault (MMF). To avoid this, we can create a proxy handle for semA, as follows:

   #pragma default_variable_attributes = @ ".ut2a.bss"
   SCB_PTR semAu; /* proxy handle */
   #pragma default_variable_attributes =

semAu is a proxy handle in the ut2a_data region of the MPU. Next we create semA in pmode and assign its handle to the proxy handle[1]:

   SCB_PTR  semA;

   semA = SemCreate(EVENT, 1, “semA”, &semA);
   semAu = semA;

Then in task ut2a:

   SemSignal(semAu);

runs without an MMF and successfully signals semA if ut2a has a semA token. It would appear that the semA token cannot work for ut2a because ut2a is using a proxy handle to access semA and the token is for the semA handle. However semA knows its token is &semA. So if ut2a has a semA token in its token list, it will be allowed to signal semA.

Conclusion

We have reviewed how tokens are used to improve embedded and IoT device security. Tokens are implemented in SecureSMX. See the SecureSMX GitHub repository for full source code.

References

1. “Task Runtime Limits”, LinkedIn, Ralph Moore, Sept 2026.

Notes

[1] Normally, all of umode memory can be accessed from pmode.

Search

Categories

Recent posts

October 15, 2026
Tokens Improve IoT Security
September 28, 2026
Task Runtime Limits
February 17, 2026
Portals Isolate Partitions
December 6, 2025
ptasks and utasks Improve Security