Page 1 of 1

World Controller ESP32s3

Posted: Sun Aug 18, 2024 6:52 pm
by hightechasshole
Hi

I have an application where user-provided code has to be loaded and executed (not flashed, only executed from RAM using espressif/elf_loader).
It has to be assumed that this code is malicious, and doing everything it can to destroy the device. I am especially worried about flash and efuses. The absolute minimum that i have to guarantee is that malicious code cannot:
- overwrite the factory app
- break the chip's configuration by messing with efuses

everything else (crashing, corrupting OTA app) is acceptable (though obviously better if it is also prevented).

Is the world controller secure enough for such a use-case? I was thinking about locking the flash chip after boot (but before running the untrusted code), to prevent overwriting of firmware. however it seems that esp32s3 integrated flash doesn't have this feature?

i then thought about using external, lockable flash, but realized the same problem exists with efuses.

the untrusted code will not have any syscall access, it will just read from a pre-defined memory range, and write to another pre-defined memory range. it will also be able to execute firmware-provided functions (still in untrusted mode though - those are all pure side-effect free functions like std::sin for example)

what do you think? is this possible with world controller? is there a better approach?

Re: World Controller ESP32s3

Posted: Mon Aug 19, 2024 1:39 am
by Sprite
We have some code for it here https://github.com/espressif/esp-privilege-separation but you may need to finnick with it a bit before it works, it's not really production-quality code.

Something that might be easier to get working is to integrate a webassembly interpreter into your firmware, then have the third party compile their code to webassembly.

(Either that or some domain-specific code - if you can explain a bit better what the code is supposed to do I may have a better suggestion)

Re: World Controller ESP32s3

Posted: Mon Aug 19, 2024 7:45 am
by hightechasshole
thanks for your response!

I don't mind having to spend (even significant) development time to get it running reliably. I only worry whether world controller alone is secure enough to protect the flash/efuses from malicious access.

My application is the execution of user-provided "shader" functions written in c++ which are used for 3d visualisations. elf_loader is perfect for this because I really do need the absolute best performance possible, and want to avoid interpreter overhead.

Re: World Controller ESP32s3

Posted: Mon Aug 19, 2024 8:47 am
by Sprite
Check. From what I know, the world controller is designed to stop the 'user' program from accessing all that - see the repo I linked earlier for examples.

Re: World Controller ESP32s3

Posted: Fri May 01, 2026 8:46 am
by vvb333007
Check. From what I know, the world controller is designed to stop the 'user' program from accessing all that - see the repo I linked earlier for examples.
It seems like there is an undocumented possibility to write/read any memory (including non-existent one) and read\write all 0x600dxxxx memory region. Currently I am conducting tests to see if WCL is affected by this.

BTW Here is the code to play with, function int bkdma_exec() performs these specific operations.

Code: Select all

// This code demonstrates a "backup" DMA engine use on ESP32-S3. 
//
// This peripheral is used to copy (backup) data from MMIO registers
// (both contiguous blocks and fragments) into SRAM and back. It has nothing to do with GDMA
//
// This code and information here are experimental
//
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>

extern int ets_printf(const char *, ... );


// BK DMA peripheral registers:
//
// The BK DMA MMIO region starts at address 0x6001A000 and goes at least up to 0x6001A028
// Access to this region is only possible via 32-bit aligned operations,
// so all registers are accessed as uint32_t


// Main control/configuration register BKDMA_CMD_CFG_REG
//
#define BKDMA_CMD_CFG_REG           ((volatile uint32_t *)0x6001a000)
#   define BKDMA_CC_MODE_BITMAP     8              // Enable "bitmap" mode
#   define BKDMA_CC_CMD_START       0x20000000     // Start DMA
#   define BKDMA_CC_MODE_MMIO2RAM   0x40000000     // Transfer direction
#   define BKDMA_CC_CMD_LATCH       0x80000000     // Probably: latch config into internal shadow registers

// Addresses: MMIO and SRAM for data transfer
//
#define BKDMA_MMIO_ADDR_REG         ((volatile uint32_t *)0x6001a004) // address like 0x600xxxxx
#define BKDMA_SRAM_ADDR_REG         ((volatile uint32_t *)0x6001a008) // regular RAM address

// "Bitmap" mode: not fully studied/tested, so not used here.
//
// Probably: 128 bits across 4 registers define which MMIO registers inside a 4 KB window to read.
// Each bit corresponds to one 32-bit word: 128 bits × 32 bytes = 4 KB (one memory page). Or may be not :)
//
#define BKDMA_BITMAP0_REG ((volatile uint32_t *)0x6001a00c) // bits 0..31
#define BKDMA_BITMAP1_REG ((volatile uint32_t *)0x6001a010) // bits 32..63
#define BKDMA_BITMAP2_REG ((volatile uint32_t *)0x6001a014) //
#define BKDMA_BITMAP3_REG ((volatile uint32_t *)0x6001a018) // bits ..127


// Status register (contains the IDLE bit)
//
#define BKDMA_STATUS_REG ((volatile uint32_t *)0x6001a01c)
#   define BKDMA_S_IDLE 1 // LSB: 1 = IDLE, 0 = BUSY

// Second command register: purpose unclear, but looks like an "enable"
// Automatically resets after operation completes
//
#define BKDMA_CMD2_REG     ((volatile uint32_t *)0x6001a028)
#   define BKDMA_C2_EN     0x00000001

// This register is documented in the ESP32-S3 TRM: System Configuration Register
// (TRM v1.7, section "4.3.5.1 Module/Peripheral Address Mapping")
// Used to enable/disable clocking for the backup DMA peripheral
//
#define SYSTEM_PERIP_CLK_EN1_REG  ((volatile uint32_t *)0x600c001c)
#   define SYSTEM_PERI_BACKUP_CLK_EN 0x00000001

// Copy a memory block between MMIO registers and SRAM
//
// Example: copy 0x6003500 ... 0x6003510 into a buffer:
//    bkdma_exec((void *)0x6003500, my_buf, 4, true);
//
// Example: write buffer into 0x6003500 ... 0x6003510
//    bkdma_exec((void *)0x6003500, my_buf, 4, false);
//
// NOTE1: does not raise exceptions or generate interrupts, even if `ram` address is invalid
// NOTE2: if `mmio` is not within 0x6xxxxxxx range, the function effectively behaves like
//        a "special" memset(mmio, 0, count * 4). No exceptions are raised.
//
int bkdma_exec(void   *mmio,     // `mmio`     : MMIO address, e.g. 0x60035000
               void   *ram,      // `ram`      : SRAM address (e.g. static buffer)
               uint8_t count,    // `count`    : number of 32-bit words
               bool    mmio2ram  // `mmio2ram` : true = MMIO→SRAM, false = SRAM→MMIO
              ) {

  uint32_t tmp;

  // Parameter validation: alignment, non-zero size
  //
  if (mmio == NULL               ||
      ram == NULL                ||
      count == 0                 ||
      ((uintptr_t)mmio & 3) != 0 ||
      ((uintptr_t)ram & 3) != 0) {
      
    ets_printf("bkdma_exec(): bad count / bad address / bad alignment: %x, %x, %u\r\n",
               (unsigned int)mmio, (unsigned int)ram, count);
    return -1;
  }

  // Enable BK DMA clock
  *SYSTEM_PERIP_CLK_EN1_REG = *SYSTEM_PERIP_CLK_EN1_REG | SYSTEM_PERI_BACKUP_CLK_EN;

  // Configure "simple" mode (no bitmap)
  *BKDMA_CMD_CFG_REG = *BKDMA_CMD_CFG_REG & ~BKDMA_CC_MODE_BITMAP;
  
  // Set transfer direction (MMIO→SRAM or SRAM→MMIO)
  if (mmio2ram)
    tmp = *BKDMA_CMD_CFG_REG | BKDMA_CC_MODE_MMIO2RAM;
  else
    tmp = *BKDMA_CMD_CFG_REG & ~BKDMA_CC_MODE_MMIO2RAM;
  *BKDMA_CMD_CFG_REG = tmp;
    
  // Write MMIO and SRAM addresses
  *BKDMA_MMIO_ADDR_REG = (uintptr_t)mmio;
  *BKDMA_SRAM_ADDR_REG = (uintptr_t)ram;

  // Set transfer size (in 32-bit words), 10-bit field → up to ~4 KB
  *BKDMA_CMD_CFG_REG = (*BKDMA_CMD_CFG_REG & 0xE007FFFF) | ((count << 19) & 0x1FF80000);

  // Prepare and start:
  // Naming might be off - I basically made it up based on observed behavior.
  // Well, not entirely made up - reverse-engineered the logic a bit, but still could be wrong.
  // Doesn’t affect functionality though.
  // I don’t know exactly which bit starts the DMA, but to trigger it,
  // all three writes must be done in this exact order.
  //
  *BKDMA_CMD2_REG    = *BKDMA_CMD2_REG    | BKDMA_C2_EN;
  *BKDMA_CMD_CFG_REG = *BKDMA_CMD_CFG_REG | BKDMA_CC_CMD_LATCH;
  *BKDMA_CMD_CFG_REG = *BKDMA_CMD_CFG_REG | BKDMA_CC_CMD_START;

  // Wait for completion (IDLE)
  do { /* spin */ } while ((*BKDMA_STATUS_REG & BKDMA_S_IDLE) == 0);

  // Stop and "de-initialize"
  *BKDMA_CMD_CFG_REG = *BKDMA_CMD_CFG_REG & ~BKDMA_CC_CMD_START;
  *BKDMA_CMD_CFG_REG = *BKDMA_CMD_CFG_REG & ~BKDMA_CC_CMD_LATCH;

  // Disable BK DMA clock
  *SYSTEM_PERIP_CLK_EN1_REG = *SYSTEM_PERIP_CLK_EN1_REG & (~SYSTEM_PERI_BACKUP_CLK_EN);

  // Done!
  return 0;
}

Re: World Controller ESP32s3

Posted: Wed May 06, 2026 2:13 pm
by ok-home
You can read the section
Chapter 15 Permission Control (PMS)
https://documentation.espressif.com/esp ... ual_en.pdf
You can lock any block of registers and memory sections from being modified, and then block the ability to unlock these sections until the next processor reset.

In particular, simply lock the efuze,,,

Re: World Controller ESP32s3

Posted: Thu May 07, 2026 2:38 pm
by vvb333007

You can lock any block of registers
If by registers you mean MMIO - no, you can not completely lock them.

What you can do is to block access to BKDMA and then block access to your MMIOs of interest. But this will break PM/sleep feature in BT/WiFi: radio will not be able to wake up properly (it uses BKDMA to bulk restore registers)

Re: World Controller ESP32s3

Posted: Thu May 07, 2026 10:51 pm
by MicroController
What you can do is to block access to BKDMA and then block access to your MMIOs of interest. But this will break PM/sleep feature in BT/WiFi: radio will not be able to wake up properly (it uses BKDMA to bulk restore registers)
Yup, the world controller is not supported by the IDF. If it were fully supported, there would be trusted/system tasks, like WiFi, which would be allowed to access "dangerous" registers while "user" tasks would not.

Re: World Controller ESP32s3

Posted: Fri May 08, 2026 7:37 am
by vvb333007
there would be trusted/system tasks, like WiFi, which would be allowed to access "dangerous" registers while "user" tasks would not.
Yeah, "trusted" tasks without memory protection :).

What user code can do in this case? Simply deinit wifi, replace `g_osi_funcs._task_create_pinned_to_core` with a pointer to your function, start the wifi again: congrats, you just got execution control right from SecureWorld :).

No, all this WCL idea is not viable unless you have memory protection on page basis. Means - proper MMU. Why not to remove ASSIST_DEBUG and WCL and use saved silicone space to implement proper MMU? A big question.

Re: World Controller ESP32s3

Posted: Fri May 08, 2026 8:24 am
by MicroController
No, all this WCL idea is not viable unless you have memory protection on page basis. Means - proper MMU.
Of course, the WCL has to be used in conjunction with the "Permission Controller" (PMS) which provides the memory and peripheral access protection. - As I said, IDF/FreeRTOS don't really support WCL or PMS, which is why even if you "manually" set up the WCL you still don't get the appropriate memory protection to make it secure. - The hardware is there, just not the software.