Skip to content

Ioto Safe Runtime (R) ​

Ioto is built upon the Safe Runtime (R), a secure, high-performance C runtime library designed specifically for embedded IoT applications. This foundational layer insulates Ioto from the underlying platform and enables portability across operating systems and hardware.

Ioto Architecture

Design Goals ​

The Safe Runtime is built around these core principles:

  1. Security First — Mitigate buffer overflows, memory corruption, and other common C vulnerabilities
  2. Embedded Optimization — Ultra-compact footprint suitable for resource-constrained devices
  3. Simplicity — Null-tolerant APIs that reduce boilerplate error checking
  4. Portability — Cross-platform support via OS abstraction layer (Linux, macOS, Windows, ESP32, FreeRTOS, VxWorks)
  5. Concurrency — Lightweight fiber coroutines instead of heavyweight OS threads
  6. Performance — Zero-copy operations and optimized data structures

Architecture Overview ​

The Safe Runtime follows a layered architecture with modular components:

┌─────────────────────────────────────────────────────────────┐
│                    Application Layer                        │
│         (IoT Agent, Web Server, MQTT Client, etc.)          │
└─────────────────────────────────────────────────────────────┘
                            ▲
┌─────────────────────────────────────────────────────────────┐
│                  Safe Runtime (R) Library                   │
│ ┌─────────────┐  ┌──────────────┐  ┌────────────────────┐   │
│ │   Fiber     │  │   Event      │  │   I/O & Network    │   │
│ │ Coroutines  │  │    Loop      │  │   (Socket, File)   │   │
│ └─────────────┘  └──────────────┘  └────────────────────┘   │
│ ┌─────────────┐  ┌──────────────┐  ┌────────────────────┐   │
│ │   Memory    │  │     Data     │  │   Safe Strings     │   │
│ │ Management  │  │  Structures  │  │   & Buffers        │   │
│ └─────────────┘  └──────────────┘  └────────────────────┘   │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │        Platform Abstraction Layer (osdep)               │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
                            ▲
┌─────────────────────────────────────────────────────────────┐
│                Operating System / RTOS                      │
│         (Linux, macOS, Windows, FreeRTOS, etc.)             │
└─────────────────────────────────────────────────────────────┘

Core Subsystems ​

The Safe Runtime provides these key subsystems:

SubsystemDescription
Runtime CoreInitialization, lifecycle management, and service coordination
Memory ManagementCentralized allocation with automatic failure handling
Fiber SystemCooperative multitasking with lightweight coroutines
Event SystemTime-based and I/O event scheduling
Data StructuresLists, hash tables, red-black trees, dynamic buffers
String OperationsSafe string functions that prevent buffer overflows
I/O LayerFiber-aware file and socket operations with TLS support
Platform AbstractionOS-specific implementations unified under common API

Key Security Features ​

The Safe Runtime replaces standard C library functions with secure alternatives:

Standard CSafe RuntimeProtection
strlen()slen()NULL tolerance
strcpy()scopy(dest, size, src)Bounds checking
strcat()scat(dest, size, src)Bounds checking
strcmp()scmp(s1, s2)NULL tolerance
sprintf()sfmt(fmt, ...)Dynamic allocation
strdup()sclone(str)NULL returns empty string
malloc()/free()rAlloc()/rFree()Centralized failure handling

Additional Security Features:

  • Buffer Overflow Prevention — Bounds-checked string operations
  • Memory Safety — Centralized allocation prevents leaks and double-frees
  • Stack Protection — Guard pages and overflow detection for fiber stacks
  • TLS Support — OpenSSL and MbedTLS integration for secure communications

TLS Session Resumption ​

For faster TLS reconnections to the same server, cache and reuse TLS sessions:

c
// After successful connection, save session
RSession *session = rGetTlsSession(sock);

// On reconnect, apply cached session before connecting
rSetTlsSession(newSock, session);
rConnectSocket(newSock, host, port, 0);

// When done with cached session
rFreeTlsSession(session);

Session resumption avoids the full TLS handshake, reducing connection time significantly for repeated connections to the same server.

Memory Footprint ​

The runtime is optimized for embedded devices:

ConfigurationSizeUse Case
Minimal~50KBCore runtime + memory + strings only
Typical IoT~200KBFull runtime with fibers, events, sockets, TLS
Full~400KBAll features enabled including OpenSSL

Centralized Memory Allocator ​

The Safe Runtime uses a centralized memory allocation strategy with a global error handler. This design simplifies application code by eliminating the need for individual NULL checks after every allocation.

How It Works ​

All memory allocations go through the rAlloc() family of functions:

FunctionDescription
rAlloc(size)Allocate memory, aligned to 8-byte boundary
rAllocZeroed(size)Allocate and zero-initialize
rAllocType(Type)Allocate structure with type safety
rRealloc(ptr, size)Resize allocation
rFree(ptr)Free memory (NULL-safe)

When memory allocation fails, the runtime invokes a centralized memory handler rather than returning NULL. By default, this handler terminates the application — a safe behavior for embedded systems where memory exhaustion typically indicates an unrecoverable state.

Benefits ​

Simplified Code: Applications don't need to check every allocation for NULL:

c
// Traditional C - requires NULL check after every allocation
char *buf = malloc(1024);
if (buf == NULL) {
    // handle error
}

// Safe Runtime - no NULL check needed
char *buf = rAlloc(1024);
// If this returns, allocation succeeded

Single Point of Failure Handling: All allocation failures are handled in one place, making it easier to implement consistent error recovery or logging.

Custom Memory Handler ​

Applications can install a custom memory handler using rSetMemHandler() to implement alternative failure strategies such as logging, cleanup, or graceful degradation:

c
static void myMemHandler(size_t size) {
    rError("memory", "Allocation failed for %zu bytes", size);
    // Perform cleanup or attempt recovery
    exit(1);
}

// Install custom handler
rSetMemHandler(myMemHandler);

Ioto Types ​

The Ioto Agent uses an operating system dependent layer called "osdep".

By including "osdep.h", you gain access to these cross-platform types. Some types are implemented natively on some platforms and others are provided by "osdep".

Quick Tour ​

Here are some primitive data types:

c
bool   b;
int8   i8;
int16  i16;
int32  i32;
int64  i64;
uint8  u8;
uint16 u16;
uint32 u32;
uint64 u64;
schar  c;      //  Signed char
uchar  c;      //  Unsigned char
cchar  c;      //  Const char
cuchar  c;     //  Const unsigned char

Other useful types:

c
ssize   length;     //  64-bit signed length
Time    time;       //  Time in milliseconds since Jan 1, 1970
Ticks   ticks;      //  Elapsed time in system millisecond ticks (never goes backwards)

For a full list of the osdep types, see: OSDEP API

Return Values ​

Many Ioto APIs return an integer value that is zero for success and otherwise set to a negative error code.

Here are the return codes used by the Ioto runtime:

c
#define R_ERR_OK              0
#define R_ERR_BASE            -1
#define R_ERR                 -2
#define R_ERR_ABORTED         -3
#define R_ERR_ALREADY_EXISTS  -4
#define R_ERR_BAD_ACK         -5
#define R_ERR_BAD_ARGS        -6
#define R_ERR_BAD_DATA        -7
#define R_ERR_BAD_FORMAT      -8
#define R_ERR_BAD_HANDLE      -9
#define R_ERR_BAD_NULL        -10
#define R_ERR_BAD_REQUEST     -11
#define R_ERR_BAD_RESPONSE    -12
#define R_ERR_BAD_SESSION     -13
#define R_ERR_BAD_STATE       -14
#define R_ERR_BAD_SYNTAX      -15
#define R_ERR_BAD_TYPE        -16
#define R_ERR_BAD_VALUE       -17
#define R_ERR_BUSY            -18
#define R_ERR_CANT_ACCESS     -19
#define R_ERR_CANT_ALLOCATE   -20
#define R_ERR_CANT_COMPLETE   -21
#define R_ERR_CANT_CONNECT    -22
#define R_ERR_CANT_CREATE     -23
#define R_ERR_CANT_DELETE     -24
#define R_ERR_CANT_FIND       -25
#define R_ERR_CANT_INITIALIZE -26
#define R_ERR_CANT_LOAD       -27
#define R_ERR_CANT_OPEN       -28
#define R_ERR_CANT_READ       -29
#define R_ERR_CANT_WRITE      -30
#define R_ERR_DELETED         -31
#define R_ERR_MEMORY          -32
#define R_ERR_NETWORK         -33
#define R_ERR_NOT_CONNECTED   -34
#define R_ERR_NOT_INITIALIZED -35
#define R_ERR_NOT_READY       -36
#define R_ERR_READ_ONLY       -37
#define R_ERR_TIMEOUT         -38
#define R_ERR_TOO_MANY        -39
#define R_ERR_WONT_FIT        -40
#define R_ERR_WOULD_BLOCK     -41
#define R_ERR_MAX             -42

NULL Tolerance ​

Ioto adopts a technique call NULL tolerance where APIs, wherever possible, tolerate NULL arguments and attempt to do something reasonable with their arguments, rather than crashing the applications.

The standard C library is not NULL tolerant. If you pass a NULL pointer to routines such as as free() or strlen() your program will crash.

The Ioto memory allocator is NULL tolerant when calling rFree. Similarly, Ioto provides a safe string library that will accept NULL arguments wherever possible. In the case of strlen, Ioto provides slen() which will return zero if passed a NULL pointer.