Files
arm-trusted-firmware/common/smc_validation.c
T
kennethkabogo 92d6ae607e feat(common): introduce SMC validation framework
This patch introduces a standardized framework for validating SiP SMC
arguments in EL3 runtime services to mitigate Privilege-Blind Forwarding
(PBF) and TOCTOU race conditions.

The framework provides:
- Atomic Shadow-Copy with smc_get_mem_range() inline function
- Type-safe validation using uintptr_t and size_t
- Integer overflow protection for memory range tuples
- Architecture-aware register mapping in common/smc_validation.c

Change-Id: Icf8f658c16fb25989eb757b71f70668c787c3153
Signed-off-by: Kenneth Kabogo <kennethkabogo2@gmail.com>
2026-05-05 18:28:45 +03:00

95 lines
2.3 KiB
C

/*
* Copyright (c) 2026, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdint.h>
#include <common/smc_validation_framework.h>
#include <context.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/smccc.h>
#include <lib/utils_def.h>
#include <smccc_helpers.h>
#pragma weak plat_is_valid_ns_address_range
bool plat_is_valid_ns_address_range(uintptr_t base, size_t size)
{
return true;
}
int smc_get_arg(void *handle, unsigned int arg_idx, u_register_t *val)
{
#ifdef __aarch64__
static const uint16_t x_offsets[] = {
CTX_GPREG_X0, CTX_GPREG_X1, CTX_GPREG_X2, CTX_GPREG_X3,
CTX_GPREG_X4, CTX_GPREG_X5, CTX_GPREG_X6, CTX_GPREG_X7,
CTX_GPREG_X8, CTX_GPREG_X9, CTX_GPREG_X10, CTX_GPREG_X11,
CTX_GPREG_X12, CTX_GPREG_X13, CTX_GPREG_X14, CTX_GPREG_X15,
CTX_GPREG_X16, CTX_GPREG_X17
};
#else
static const uint16_t x_offsets[] = { CTX_GPREG_R0, CTX_GPREG_R1,
CTX_GPREG_R2, CTX_GPREG_R3 };
#endif
/* SMCCC registers are x1 to x17 (AArch64) or r1 to r7 (AArch32). */
if (arg_idx >= ARRAY_SIZE(x_offsets)) {
return SMC_INVALID_PARAM;
}
#ifdef __aarch64__
*val = (u_register_t)read_ctx_reg(get_gpregs_ctx(handle),
x_offsets[arg_idx]);
#else
*val = (u_register_t)read_ctx_reg(get_regs_ctx(handle),
x_offsets[arg_idx]);
#endif
return SMC_OK;
}
int smc_validate_mem_range(uintptr_t base, size_t size)
{
/* Reject if base + size overflows. */
if ((size > 0U) && check_uptr_overflow(base, (uintptr_t)size)) {
return SMC_INVALID_PARAM;
}
/* Reject if the range is not in Non-Secure memory. */
if (!plat_is_valid_ns_address_range(base, size)) {
return SMC_INVALID_PARAM;
}
return SMC_OK;
}
int smc_get_mem_range(void *handle, unsigned int base_reg,
unsigned int size_reg, uintptr_t *base, size_t *size)
{
u_register_t base_reg_val;
u_register_t size_reg_val;
if (smc_get_arg(handle, base_reg, &base_reg_val) != SMC_OK) {
return SMC_INVALID_PARAM;
}
if (smc_get_arg(handle, size_reg, &size_reg_val) != SMC_OK) {
return SMC_INVALID_PARAM;
}
uintptr_t base_local = (uintptr_t)base_reg_val;
size_t size_local = (size_t)size_reg_val;
if (smc_validate_mem_range(base_local, size_local) != SMC_OK) {
return SMC_INVALID_PARAM;
}
*base = base_local;
*size = size_local;
return SMC_OK;
}