Introduction | Theory | Lab | Course Home
This laboratory provides comprehensive hands-on experience with Zephyr’s user mode capabilities. You’ll implement secure applications using memory domains, system calls, and advanced security features.
# Supported boards for user mode labs
supported_boards:
- frdm_k64f # ARM Cortex-M4 with MPU
- nucleo_f767zi # ARM Cortex-M7 with MPU
- qemu_cortex_m3 # QEMU emulation with MPU
- arduino_due # ARM Cortex-M3 with MPU
- mimxrt1050_evk # ARM Cortex-M7 with MPU
# Required Kconfig options
required_config:
- CONFIG_USERSPACE=y
- CONFIG_APPLICATION_MEMORY=y
- CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT=y
- CONFIG_MPU_GAP_FILLING=y
# Set up lab workspace
cd ~/zephyr-workspace
mkdir -p user_mode_labs
cd user_mode_labs
# Create lab structure
mkdir -p {lab1_basics,lab2_domains,lab3_syscalls,lab4_security,lab5_advanced}
mkdir -p shared/{drivers,libraries,configs}
Create basic user mode threads and understand privilege separation.
Create lab1_basics/src/main.c:
/*
* Lab 1: User Mode Basics
*
* This lab demonstrates:
* - Creating user mode threads
* - Understanding privilege levels
* - Basic memory protection
*/
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(user_mode_lab1, LOG_LEVEL_INF);
// Define stack for user thread
#define USER_STACK_SIZE 2048
K_THREAD_STACK_DEFINE(user_stack, USER_STACK_SIZE);
// User thread structure
static struct k_thread user_thread_data;
// Simple user mode function
void user_thread_function(void *arg1, void *arg2, void *arg3)
{
int counter = 0;
LOG_INF("User thread started (ID: %p)", k_current_get());
LOG_INF("Thread running in %s mode",
k_is_user_context() ? "USER" : "SUPERVISOR");
while (1) {
// This will work - user threads can call system calls
printk("User thread iteration %d (uptime: %llu ms)\n",
counter++, k_uptime_get());
// Attempt to access kernel memory (this should fail)
// Uncomment the following line to see the protection in action
// *(volatile uint32_t *)0x20000000 = 0xDEADBEEF;
k_sleep(K_SECONDS(2));
}
}
// Privileged operation that user threads cannot perform directly
void privileged_operation(void)
{
LOG_INF("Performing privileged operation in supervisor mode");
// Access system control registers (privileged operation)
uint32_t control_reg = __get_CONTROL();
LOG_INF("CONTROL register value: 0x%08x", control_reg);
// Check privilege level
if (control_reg & 0x1) {
LOG_INF("Currently running in unprivileged (user) mode");
} else {
LOG_INF("Currently running in privileged (supervisor) mode");
}
}
int main(void)
{
LOG_INF("=== User Mode Fundamentals Lab ===");
// Demonstrate supervisor mode operations
LOG_INF("Main thread running in %s mode",
k_is_user_context() ? "USER" : "SUPERVISOR");
privileged_operation();
// Create user mode thread
k_tid_t user_tid = k_thread_create(&user_thread_data,
user_stack,
K_THREAD_STACK_SIZEOF(user_stack),
user_thread_function,
NULL, NULL, NULL,
5, K_USER, K_NO_WAIT);
LOG_INF("Created user thread: %p", user_tid);
// Main thread continues in supervisor mode
int main_counter = 0;
while (1) {
printk("Main (supervisor) thread iteration %d\n", main_counter++);
// Supervisor can perform privileged operations
privileged_operation();
k_sleep(K_SECONDS(3));
}
return 0;
}
Create lab1_basics/prj.conf:
# Basic user mode configuration
CONFIG_USERSPACE=y
CONFIG_APPLICATION_MEMORY=y
# Memory protection
CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT=y
CONFIG_MPU_GAP_FILLING=y
# Logging configuration
CONFIG_LOG=y
CONFIG_LOG_MODE_IMMEDIATE=y
CONFIG_PRINTK=y
# Thread configuration
CONFIG_MAIN_STACK_SIZE=2048
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
# Disable features that may conflict with user mode or require special handling
# Note: Some debugging features might be disabled in user mode to prevent
# information leaks from kernel space or because they require specific
# configurations when user mode is active.
CONFIG_THREAD_MONITOR=n
CONFIG_THREAD_NAME=n
Create lab1_basics/CMakeLists.txt:
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(user_mode_basics)
target_sources(app PRIVATE src/main.c)
*** Booting Zephyr OS build v4.2.99 ***
[00:00:00.000,000] <inf> user_mode_lab1: === User Mode Fundamentals Lab ===
[00:00:00.000,000] <inf> user_mode_lab1: Main thread running in SUPERVISOR mode
[00:00:00.000,000] <inf> user_mode_lab1: Performing privileged operation in supervisor mode
[00:00:00.000,000] <inf> user_mode_lab1: CONTROL register value: 0x00000000
[00:00:00.000,000] <inf> user_mode_lab1: Currently running in privileged (supervisor) mode
[00:00:00.000,000] <inf> user_mode_lab1: Created user thread: 0x20001234
[00:00:00.000,000] <inf> user_mode_lab1: User thread started (ID: 0x20001234)
[00:00:00.000,000] <inf> user_mode_lab1: Thread running in USER mode
User thread iteration 0 (uptime: 0 ms)
Main (supervisor) thread iteration 0
[00:00:00.000,000] <inf> user_mode_lab1: Performing privileged operation in supervisor mode
User thread iteration 1 (uptime: 2000 ms)
Implement memory isolation using domains and partitions.
Create lab2_domains/src/main.c:
/*
* Lab 2: Memory Domains and Partitions
*
* This lab demonstrates:
* - Creating memory domains
* - Defining memory partitions
* - Controlling access between user threads
*/
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/logging/log.h>
#include <zephyr/random/random.h>
LOG_MODULE_REGISTER(user_mode_lab2, LOG_LEVEL_INF);
// Shared data structures for different security levels
struct sensitive_data {
uint32_t secret_key;
uint32_t access_count;
char description[64];
};
struct public_data {
uint32_t public_counter;
char message[128];
};
// Allocate data in different memory sections
__in_section(".app_data_sensitive")
static struct sensitive_data sensitive_info = {
.secret_key = 0x12345678,
.access_count = 0,
.description = "Sensitive information - restricted access"
};
__in_section(".app_data_public")
static struct public_data public_info = {
.public_counter = 0,
.message = "Public information - unrestricted access"
};
// Memory partitions for different access levels
K_MEM_PARTITION_DEFINE(sensitive_partition,
&sensitive_info,
sizeof(sensitive_info),
K_MEM_PARTITION_P_RW_U_RW);
K_MEM_PARTITION_DEFINE(public_partition,
&public_info,
sizeof(public_info),
K_MEM_PARTITION_P_RW_U_RW);
// Memory domains for different security contexts
static struct k_mem_domain trusted_domain;
static struct k_mem_domain restricted_domain;
// Thread stacks
#define THREAD_STACK_SIZE 2048
K_THREAD_STACK_DEFINE(trusted_stack, THREAD_STACK_SIZE);
K_THREAD_STACK_DEFINE(restricted_stack, THREAD_STACK_SIZE);
static struct k_thread trusted_thread_data;
static struct k_thread restricted_thread_data;
// Trusted thread - can access sensitive data
void trusted_thread(void *arg1, void *arg2, void *arg3)
{
LOG_INF("Trusted thread started (can access sensitive data)");
while (1) {
// Access sensitive data (should succeed)
sensitive_info.access_count++;
sensitive_info.secret_key ^= sys_rand32_get();
// Access public data (should also succeed)
public_info.public_counter++;
snprintf(public_info.message, sizeof(public_info.message),
"Updated by trusted thread (iteration %u)",
public_info.public_counter);
LOG_INF("Trusted: Sensitive access count = %u, key = 0x%08x",
sensitive_info.access_count, sensitive_info.secret_key);
LOG_INF("Trusted: Public counter = %u, message = '%s'",
public_info.public_counter, public_info.message);
k_sleep(K_SECONDS(3));
}
}
// Restricted thread - cannot access sensitive data
void restricted_thread(void *arg1, void *arg2, void *arg3)
{
LOG_INF("Restricted thread started (limited access)");
while (1) {
// Access public data (should succeed)
public_info.public_counter += 10;
snprintf(public_info.message, sizeof(public_info.message),
"Updated by restricted thread (counter %u)",
public_info.public_counter);
LOG_INF("Restricted: Public counter = %u, message = '%s'",
public_info.public_counter, public_info.message);
// Attempt to access sensitive data (should fail)
LOG_INF("Restricted: Attempting to access sensitive data...");
// Uncomment the following lines to see memory protection in action
/*
LOG_INF("Restricted: Sensitive access count = %u",
sensitive_info.access_count);
sensitive_info.secret_key = 0xDEADBEEF;
*/
k_sleep(K_SECONDS(4));
}
}
// Initialize memory domains
int setup_memory_domains(void)
{
int ret;
// Initialize trusted domain (can access both partitions)
ret = k_mem_domain_init(&trusted_domain, 0, NULL);
if (ret != 0) {
LOG_ERR("Failed to initialize trusted domain: %d", ret);
return ret;
}
ret = k_mem_domain_add_partition(&trusted_domain, &sensitive_partition);
if (ret != 0) {
LOG_ERR("Failed to add sensitive partition to trusted domain: %d", ret);
return ret;
}
ret = k_mem_domain_add_partition(&trusted_domain, &public_partition);
if (ret != 0) {
LOG_ERR("Failed to add public partition to trusted domain: %d", ret);
return ret;
}
// Initialize restricted domain (can only access public partition)
ret = k_mem_domain_init(&restricted_domain, 0, NULL);
if (ret != 0) {
LOG_ERR("Failed to initialize restricted domain: %d", ret);
return ret;
}
ret = k_mem_domain_add_partition(&restricted_domain, &public_partition);
if (ret != 0) {
LOG_ERR("Failed to add public partition to restricted domain: %d", ret);
return ret;
}
LOG_INF("Memory domains configured successfully");
return 0;
}
int main(void)
{
LOG_INF("=== Memory Domains and Partitions Lab ===");
// Initialize memory domains
int ret = setup_memory_domains();
if (ret != 0) {
LOG_ERR("Memory domain setup failed: %d", ret);
return ret;
}
// Create trusted thread with access to sensitive data
k_tid_t trusted_tid = k_thread_create(&trusted_thread_data,
trusted_stack,
K_THREAD_STACK_SIZEOF(trusted_stack),
trusted_thread,
NULL, NULL, NULL,
5, K_USER, K_NO_WAIT);
ret = k_mem_domain_add_thread(&trusted_domain, trusted_tid);
if (ret != 0) {
LOG_ERR("Failed to add trusted thread to domain: %d", ret);
return ret;
}
// Create restricted thread with limited access
k_tid_t restricted_tid = k_thread_create(&restricted_thread_data,
restricted_stack,
K_THREAD_STACK_SIZEOF(restricted_stack),
restricted_thread,
NULL, NULL, NULL,
5, K_USER, K_NO_WAIT);
ret = k_mem_domain_add_thread(&restricted_domain, restricted_tid);
if (ret != 0) {
LOG_ERR("Failed to add restricted thread to domain: %d", ret);
return ret;
}
LOG_INF("Created threads: trusted=%p, restricted=%p",
trusted_tid, restricted_tid);
// Main thread monitors the system
while (1) {
LOG_INF("=== System Status ===");
LOG_INF("Sensitive data: access_count=%u, key=0x%08x",
sensitive_info.access_count, sensitive_info.secret_key);
LOG_INF("Public data: counter=%u, message='%s'",
public_info.public_counter, public_info.message);
k_sleep(K_SECONDS(10));
}
return 0;
}
To ensure our sensitive_info and public_info data structures are placed in distinct, non-overlapping memory regions that we can control with memory partitions, we need to instruct the linker where to place them. This is done using a custom linker script file.
Create lab2_domains/app.ld:
/*
* Custom linker script for memory domain lab
* Defines separate sections for different access levels
*/
SECTIONS
{
/* Sensitive data section - restricted access */
.app_data_sensitive :
{
_app_data_sensitive_start = .;
*(.app_data_sensitive)
*(.app_data_sensitive.*)
_app_data_sensitive_end = .;
} > RAM
/* Public data section - unrestricted access */
.app_data_public :
{
_app_data_public_start = .;
*(.app_data_public)
*(.app_data_public.*)
_app_data_public_end = .;
} > RAM
}
INSERT AFTER .bss;
[00:00:00.000,000] <inf> user_mode_lab2: === Memory Domains and Partitions Lab ===
[00:00:00.000,000] <inf> user_mode_lab2: Memory domains configured successfully
[00:00:00.000,000] <inf> user_mode_lab2: Created threads: trusted=0x20001234, restricted=0x20001456
[00:00:00.000,000] <inf> user_mode_lab2: Trusted thread started (can access sensitive data)
[00:00:00.000,000] <inf> user_mode_lab2: Restricted thread started (limited access)
[00:00:03.000,000] <inf> user_mode_lab2: Trusted: Sensitive access count = 1, key = 0x87654321
[00:00:03.000,000] <inf> user_mode_lab2: Trusted: Public counter = 1, message = 'Updated by trusted thread (iteration 1)'
[00:00:04.000,000] <inf> user_mode_lab2: Restricted: Public counter = 11, message = 'Updated by restricted thread (counter 11)'
[00:00:04.000,000] <inf> user_mode_lab2: Restricted: Attempting to access sensitive data...
Implement custom system calls for secure inter-domain communication.
Create lab3_syscalls/src/syscalls.c:
/*
* Lab 3: Custom System Calls
*
* This lab demonstrates:
* - Defining custom system calls
* - Parameter validation
* - Secure privilege escalation
*/
#include <zephyr/kernel.h>
#include <zephyr/syscall.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util.h>
#include <zephyr/random/random.h>
LOG_MODULE_REGISTER(syscalls, LOG_LEVEL_DBG);
// Secure data structure managed by supervisor
struct secure_counter {
uint32_t value;
uint32_t access_count;
k_tid_t last_accessor;
uint64_t last_access_time;
uint32_t checksum;
};
static struct secure_counter global_counter = {0};
static struct k_spinlock counter_lock;
// Calculate checksum for integrity verification
static uint32_t calculate_checksum(const struct secure_counter *counter)
{
uint32_t checksum = 0;
checksum ^= counter->value;
checksum ^= counter->access_count;
checksum ^= (uint32_t)(uintptr_t)counter->last_accessor;
checksum ^= (uint32_t)(counter->last_access_time & 0xFFFFFFFF);
checksum ^= (uint32_t)(counter->last_access_time >> 32);
return checksum;
}
// System call: Get secure counter value
__syscall uint32_t secure_counter_get(void);
uint32_t z_impl_secure_counter_get(void)
{
k_spinlock_key_t key = k_spin_lock(&counter_lock);
// Verify integrity
uint32_t expected_checksum = calculate_checksum(&global_counter);
if (global_counter.checksum != expected_checksum) {
LOG_ERR("Counter integrity check failed!");
k_spin_unlock(&counter_lock, key);
return 0; // Return safe value
}
// Update access information
global_counter.access_count++;
global_counter.last_accessor = k_current_get();
global_counter.last_access_time = k_uptime_get();
global_counter.checksum = calculate_checksum(&global_counter);
uint32_t value = global_counter.value;
k_spin_unlock(&counter_lock, key);
LOG_DBG("Counter read: value=%u, access_count=%u",
value, global_counter.access_count);
return value;
}
// System call: Increment secure counter
__syscall int secure_counter_increment(uint32_t delta);
int z_impl_secure_counter_increment(uint32_t delta)
{
// Validate delta parameter
if (delta == 0 || delta > 1000) {
LOG_WRN("Invalid delta value: %u", delta);
return -EINVAL;
}
k_spinlock_key_t key = k_spin_lock(&counter_lock);
// Verify integrity
uint32_t expected_checksum = calculate_checksum(&global_counter);
if (global_counter.checksum != expected_checksum) {
LOG_ERR("Counter integrity check failed!");
k_spin_unlock(&counter_lock, key);
return -EFAULT;
}
// Check for overflow
if (global_counter.value > UINT32_MAX - delta) {
LOG_WRN("Counter overflow prevented");
k_spin_unlock(&counter_lock, key);
return -ERANGE;
}
// Update counter
global_counter.value += delta;
global_counter.access_count++;
global_counter.last_accessor = k_current_get();
global_counter.last_access_time = k_uptime_get();
global_counter.checksum = calculate_checksum(&global_counter);
k_spin_unlock(&counter_lock, key);
LOG_DBG("Counter incremented: delta=%u, new_value=%u",
delta, global_counter.value);
return 0;
}
// System call: Get counter statistics
__syscall int secure_counter_get_stats(struct counter_stats *stats);
struct counter_stats {
uint32_t current_value;
uint32_t access_count;
k_tid_t last_accessor;
uint64_t last_access_time;
};
int z_impl_secure_counter_get_stats(struct counter_stats *stats)
{
// Validate output buffer
if (k_usermode_buffer_validate(stats, sizeof(*stats), true) != 0) {
LOG_ERR("Invalid stats buffer");
return -EFAULT;
}
k_spinlock_key_t key = k_spin_lock(&counter_lock);
// Verify integrity
uint32_t expected_checksum = calculate_checksum(&global_counter);
if (global_counter.checksum != expected_checksum) {
LOG_ERR("Counter integrity check failed!");
k_spin_unlock(&counter_lock, key);
return -EFAULT;
}
// Copy statistics
stats->current_value = global_counter.value;
stats->access_count = global_counter.access_count;
stats->last_accessor = global_counter.last_accessor;
stats->last_access_time = global_counter.last_access_time;
k_spin_unlock(&counter_lock, key);
LOG_DBG("Stats retrieved by thread %p", k_current_get());
return 0;
}
// System call: Reset counter with authentication
__syscall int secure_counter_reset(uint32_t auth_code);
int z_impl_secure_counter_reset(uint32_t auth_code)
{
// Simple authentication (in real implementation, use proper crypto)
uint32_t expected_code = sys_rand32_get() ^ 0xDEADBEEF;
if (auth_code != expected_code) {
LOG_WRN("Authentication failed for counter reset");
return -EACCES;
}
k_spinlock_key_t key = k_spin_lock(&counter_lock);
// Reset counter
global_counter.value = 0;
global_counter.access_count = 0;
global_counter.last_accessor = k_current_get();
global_counter.last_access_time = k_uptime_get();
global_counter.checksum = calculate_checksum(&global_counter);
k_spin_unlock(&counter_lock, key);
LOG_INF("Counter reset by thread %p", k_current_get());
return 0;
}
/*
* NOTE on System Call Validation:
*
* For production-quality code, every system call must have a corresponding
* verification function (e.g., z_vrfy_secure_counter_get_stats) that validates
* all parameters passed from user space. This is a critical security measure
* to prevent malicious user threads from crashing the kernel.
*
* These verification functions have been omitted from this lab for brevity,
* but are essential for any real-world application.
*/
Create lab3_syscalls/src/main.c:
/*
* Main application using custom system calls
*/
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/logging/log.h>
#include "syscalls.h"
LOG_MODULE_REGISTER(syscall_lab, LOG_LEVEL_INF);
// Thread stacks
#define THREAD_STACK_SIZE 2048
K_THREAD_STACK_DEFINE(reader_stack, THREAD_STACK_SIZE);
K_THREAD_STACK_DEFINE(writer_stack, THREAD_STACK_SIZE);
static struct k_thread reader_thread_data;
static struct k_thread writer_thread_data;
// Reader thread - only reads counter
void reader_thread(void *arg1, void *arg2, void *arg3)
{
LOG_INF("Reader thread started");
while (1) {
// Read counter using system call
uint32_t value = secure_counter_get();
LOG_INF("Reader: Counter value = %u", value);
// Get detailed statistics
struct counter_stats stats;
int ret = secure_counter_get_stats(&stats);
if (ret == 0) {
LOG_INF("Reader: Stats - value=%u, accesses=%u, last_accessor=%p",
stats.current_value, stats.access_count, stats.last_accessor);
} else {
LOG_ERR("Reader: Failed to get stats: %d", ret);
}
k_sleep(K_SECONDS(2));
}
}
// Writer thread - modifies counter
void writer_thread(void *arg1, void *arg2, void *arg3)
{
LOG_INF("Writer thread started");
uint32_t increment_value = 1;
while (1) {
// Increment counter using system call
int ret = secure_counter_increment(increment_value);
if (ret == 0) {
LOG_INF("Writer: Successfully incremented by %u", increment_value);
} else {
LOG_ERR("Writer: Failed to increment: %d", ret);
}
// Gradually increase increment value
increment_value = (increment_value * 2) % 100 + 1;
k_sleep(K_SECONDS(3));
}
}
int main(void)
{
LOG_INF("=== Custom System Calls Lab ===");
// Initialize counter
LOG_INF("Initial counter value: %u", secure_counter_get());
// Create reader thread
k_tid_t reader_tid = k_thread_create(&reader_thread_data,
reader_stack,
K_THREAD_STACK_SIZEOF(reader_stack),
reader_thread,
NULL, NULL, NULL,
6, K_USER, K_NO_WAIT);
// Create writer thread
k_tid_t writer_tid = k_thread_create(&writer_thread_data,
writer_stack,
K_THREAD_STACK_SIZEOF(writer_stack),
writer_thread,
NULL, NULL, NULL,
5, K_USER, K_NO_WAIT);
LOG_INF("Created threads: reader=%p, writer=%p", reader_tid, writer_tid);
// Main thread periodically shows system status
int status_count = 0;
while (1) {
k_sleep(K_SECONDS(10));
LOG_INF("=== System Status #%d ===", ++status_count);
struct counter_stats stats;
int ret = secure_counter_get_stats(&stats);
if (ret == 0) {
LOG_INF("Counter: %u (accessed %u times, last by %p at %llu ms)",
stats.current_value, stats.access_count,
stats.last_accessor, stats.last_access_time);
}
}
return 0;
}
[00:00:00.000,000] <inf> syscall_lab: === Custom System Calls Lab ===
[00:00:00.000,000] <inf> syscall_lab: Initial counter value: 0
[00:00:00.000,000] <inf> syscall_lab: Created threads: reader=0x20001234, writer=0x20001456
[00:00:00.000,000] <inf> syscall_lab: Reader thread started
[00:00:00.000,000] <inf> syscall_lab: Writer thread started
[00:00:00.000,000] <inf> syscall_lab: Reader: Counter value = 0
[00:00:00.000,000] <inf> syscall_lab: Reader: Stats - value=0, accesses=2, last_accessor=0x20001234
[00:00:03.000,000] <inf> syscall_lab: Writer: Successfully incremented by 1
[00:00:02.000,000] <inf> syscall_lab: Reader: Counter value = 1
[00:00:06.000,000] <inf> syscall_lab: Writer: Successfully incremented by 2
Implement advanced security features including stack protection and secure boot verification.
Create lab4_security/src/main.c:
/*
* Lab 4: Advanced Security Features
*
* This lab demonstrates:
* - Stack canary protection
* - Buffer overflow detection
* - Secure memory patterns
*/
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/logging/log.h>
#include <zephyr/random/random.h>
#include <zephyr/sys/util.h>
LOG_MODULE_REGISTER(security_lab, LOG_LEVEL_INF);
// Security configuration
#define STACK_CANARY_SIZE 4
#define BUFFER_GUARD_SIZE 8
// Secure buffer structure with guards
struct secure_buffer {
uint32_t front_guard[BUFFER_GUARD_SIZE/4];
char data[256];
uint32_t rear_guard[BUFFER_GUARD_SIZE/4];
uint32_t checksum;
};
// Initialize buffer with security guards
void init_secure_buffer(struct secure_buffer *buf, const char *initial_data)
{
// Initialize guards with random values
for (int i = 0; i < ARRAY_SIZE(buf->front_guard); i++) {
buf->front_guard[i] = sys_rand32_get();
}
for (int i = 0; i < ARRAY_SIZE(buf->rear_guard); i++) {
buf->rear_guard[i] = sys_rand32_get();
}
// Initialize data
if (initial_data) {
strncpy(buf->data, initial_data, sizeof(buf->data) - 1);
buf->data[sizeof(buf->data) - 1] = '\0';
} else {
memset(buf->data, 0, sizeof(buf->data));
}
// Calculate checksum
buf->checksum = 0;
for (int i = 0; i < ARRAY_SIZE(buf->front_guard); i++) {
buf->checksum ^= buf->front_guard[i];
}
for (int i = 0; i < ARRAY_SIZE(buf->rear_guard); i++) {
buf->checksum ^= buf->rear_guard[i];
}
buf->checksum ^= sys_hash32(buf->data, strlen(buf->data));
}
// Check buffer integrity
bool check_buffer_integrity(const struct secure_buffer *buf)
{
// Recalculate checksum
uint32_t expected_checksum = 0;
for (int i = 0; i < ARRAY_SIZE(buf->front_guard); i++) {
expected_checksum ^= buf->front_guard[i];
}
for (int i = 0; i < ARRAY_SIZE(buf->rear_guard); i++) {
expected_checksum ^= buf->rear_guard[i];
}
expected_checksum ^= sys_hash32(buf->data, strlen(buf->data));
if (buf->checksum != expected_checksum) {
LOG_ERR("Buffer checksum mismatch: expected=0x%08x, actual=0x%08x",
expected_checksum, buf->checksum);
return false;
}
return true;
}
// Secure string copy with bounds checking
int secure_strcpy(struct secure_buffer *dst, const char *src, size_t max_len)
{
if (!check_buffer_integrity(dst)) {
LOG_ERR("Destination buffer integrity check failed");
return -EFAULT;
}
if (max_len > sizeof(dst->data)) {
LOG_ERR("Copy length exceeds buffer size");
return -EINVAL;
}
// Perform safe copy
size_t src_len = strnlen(src, max_len);
if (src_len >= sizeof(dst->data)) {
LOG_ERR("Source string too long");
return -E2BIG;
}
memcpy(dst->data, src, src_len);
dst->data[src_len] = '\0';
// Update checksum
dst->checksum = 0;
for (int i = 0; i < ARRAY_SIZE(dst->front_guard); i++) {
dst->checksum ^= dst->front_guard[i];
}
for (int i = 0; i < ARRAY_SIZE(dst->rear_guard); i++) {
dst->checksum ^= dst->rear_guard[i];
}
dst->checksum ^= sys_hash32(dst->data, strlen(dst->data));
return 0;
}
// Thread stacks with canary protection
#define THREAD_STACK_SIZE 2048
K_THREAD_STACK_DEFINE(secure_stack, THREAD_STACK_SIZE);
K_THREAD_STACK_DEFINE(test_stack, THREAD_STACK_SIZE);
static struct k_thread secure_thread_data;
static struct k_thread test_thread_data;
// Secure thread with protected operations
void secure_thread(void *arg1, void *arg2, void *arg3)
{
LOG_INF("Secure thread started with enhanced protection");
struct secure_buffer secure_buf;
init_secure_buffer(&secure_buf, "Initial secure data");
int iteration = 0;
while (1) {
// Verify buffer integrity before each operation
if (!check_buffer_integrity(&secure_buf)) {
LOG_ERR("SECURITY BREACH: Buffer corruption detected!");
k_thread_abort(k_current_get());
return;
}
// Perform secure operations
char temp_data[64];
snprintf(temp_data, sizeof(temp_data),
"Iteration %d - uptime %llu ms",
iteration++, k_uptime_get());
int ret = secure_strcpy(&secure_buf, temp_data, sizeof(temp_data));
if (ret != 0) {
LOG_ERR("Secure copy failed: %d", ret);
} else {
LOG_INF("Secure: %s", secure_buf.data);
}
k_sleep(K_SECONDS(2));
}
}
// Test thread that demonstrates security features
void test_thread(void *arg1, void *arg2, void *arg3)
{
LOG_INF("Test thread started - will attempt various operations");
struct secure_buffer test_buf;
init_secure_buffer(&test_buf, "Test buffer data");
while (1) {
// Test 1: Normal operation (should succeed)
LOG_INF("Test 1: Normal secure operation");
int ret = secure_strcpy(&test_buf, "Normal test data", 20);
LOG_INF("Test 1 result: %d, data: '%s'", ret, test_buf.data);
k_sleep(K_SECONDS(1));
// Test 2: Oversized data (should fail safely)
LOG_INF("Test 2: Oversized data test");
char large_data[300];
memset(large_data, 'A', sizeof(large_data) - 1);
large_data[sizeof(large_data) - 1] = '\0';
ret = secure_strcpy(&test_buf, large_data, sizeof(large_data));
LOG_INF("Test 2 result: %d (should be negative)", ret);
k_sleep(K_SECONDS(1));
// Test 3: Buffer integrity check
LOG_INF("Test 3: Buffer integrity verification");
bool integrity_ok = check_buffer_integrity(&test_buf);
LOG_INF("Test 3 result: Buffer integrity %s",
integrity_ok ? "OK" : "FAILED");
k_sleep(K_SECONDS(3));
}
}
int main(void)
{
LOG_INF("=== Advanced Security Features Lab ===");
// Display security configuration
LOG_INF("Security configuration:");
LOG_INF("- Stack canary size: %d bytes", STACK_CANARY_SIZE);
LOG_INF("- Buffer guard size: %d bytes", BUFFER_GUARD_SIZE);
LOG_INF("- User mode: %s", CONFIG_USERSPACE ? "enabled" : "disabled");
// Create secure thread
k_tid_t secure_tid = k_thread_create(&secure_thread_data,
secure_stack,
K_THREAD_STACK_SIZEOF(secure_stack),
secure_thread,
NULL, NULL, NULL,
5, K_USER, K_NO_WAIT);
// Create test thread
k_tid_t test_tid = k_thread_create(&test_thread_data,
test_stack,
K_THREAD_STACK_SIZEOF(test_stack),
test_thread,
NULL, NULL, NULL,
6, K_USER, K_NO_WAIT);
LOG_INF("Created threads: secure=%p, test=%p", secure_tid, test_tid);
// Main thread monitors security status
while (1) {
LOG_INF("=== Security Status Monitor ===");
LOG_INF("System uptime: %llu ms", k_uptime_get());
LOG_INF("Active threads: secure=%p, test=%p", secure_tid, test_tid);
k_sleep(K_SECONDS(10));
}
return 0;
}
[00:00:00.000,000] <inf> security_lab: === Advanced Security Features Lab ===
[00:00:00.000,000] <inf> security_lab: Security configuration:
[00:00:00.000,000] <inf> security_lab: - Stack canary size: 4 bytes
[00:00:00.000,000] <inf> security_lab: - Buffer guard size: 8 bytes
[00:00:00.000,000] <inf> security_lab: - User mode: enabled
[00:00:00.000,000] <inf> security_lab: Created threads: secure=0x20001234, test=0x20001456
[00:00:00.000,000] <inf> security_lab: Secure thread started with enhanced protection
[00:00:00.000,000] <inf> security_lab: Test thread started - will attempt various operations
[00:00:00.000,000] <inf> security_lab: Test 1: Normal secure operation
[00:00:00.000,000] <inf> security_lab: Test 1 result: 0, data: 'Normal test data'
[00:00:02.000,000] <inf> security_lab: Secure: Iteration 0 - uptime 2000 ms
[00:00:01.000,000] <inf> security_lab: Test 2: Oversized data test
[00:00:01.000,000] <inf> security_lab: Test 2 result: -27 (should be negative)
After completing these labs, students should be able to:
This comprehensive laboratory provides hands-on experience with all aspects of Zephyr’s user mode capabilities, preparing developers for real-world secure embedded system development.