kernel: mem_domain: Check for overlapping regions when considering W^X

Multiple partitions can be added to a domain, and if they overlap, they
can have different attributes.  The previous check would only check for
W^X for individual partitions, and this is insufficient.  Overlapping
partitions could have W^X attributes, but in the end, a memory region
would be writable and executable.

The way this is performed is quite "heavyweight", as it is implemented
in a O(n^2) operation.  The number of partitions per domain is small on
most devices, so this isn't an issue.  CONFIG_EXECUTE_XOR_WRITE is
still an optional feature.

Signed-off-by: Leandro Pereira <leandro.pereira@intel.com>
This commit is contained in:
Leandro Pereira 2018-02-28 14:11:41 -08:00 committed by Anas Nashif
commit db094b8d88

View file

@ -12,18 +12,62 @@
static u8_t max_partitions;
static void ensure_w_xor_x(k_mem_partition_attr_t attrs)
{
#if defined(CONFIG_EXECUTE_XOR_WRITE) && __ASSERT_ON
bool writable = K_MEM_PARTITION_IS_WRITABLE(attrs);
bool executable = K_MEM_PARTITION_IS_EXECUTABLE(attrs);
static bool sane_partition(const struct k_mem_partition *part,
const struct k_mem_partition *parts,
u32_t num_parts)
{
bool exec, write;
u32_t end;
u32_t i;
__ASSERT(writable != executable, "writable page not executable");
#else
ARG_UNUSED(attrs);
#endif
end = part->start + part->size;
exec = K_MEM_PARTITION_IS_EXECUTABLE(part->attrs);
write = K_MEM_PARTITION_IS_WRITABLE(part->attrs);
if (exec && write) {
__ASSERT(0, "partition is writable and executable <start %x>",
part->start);
return false;
}
for (i = 0; i < num_parts; i++) {
bool cur_write, cur_exec;
u32_t cur_end;
cur_end = parts[i].start + parts[i].size;
if (end < parts[i].start || cur_end < part->start) {
continue;
}
cur_write = K_MEM_PARTITION_IS_WRITABLE(parts[i].attrs);
cur_exec = K_MEM_PARTITION_IS_EXECUTABLE(parts[i].attrs);
if ((cur_write && exec) || (cur_exec && write)) {
__ASSERT(0, "overlapping partitions are "
"writable and executable "
"<%x...%x>, <%x...%x>",
part->start, end,
parts[i].start, cur_end);
return false;
}
}
return true;
}
static inline bool sane_partition_domain(const struct k_mem_domain *domain,
const struct k_mem_partition *part)
{
return sane_partition(part, domain->partitions,
domain->num_partitions);
}
#else
#define sane_partition(...) (true)
#define sane_partition_domain(...) (true)
#endif
void k_mem_domain_init(struct k_mem_domain *domain, u32_t num_parts,
struct k_mem_partition *parts[])
{
@ -43,11 +87,19 @@ void k_mem_domain_init(struct k_mem_domain *domain, u32_t num_parts,
for (i = 0; i < num_parts; i++) {
__ASSERT(parts[i], "");
ensure_w_xor_x(parts[i]->attr);
__ASSERT((parts[i]->start + parts[i]->size) >
parts[i]->start, "");
domain->partitions[i] = *parts[i];
}
#if defined(CONFIG_EXECUTE_XOR_WRITE)
for (i = 0; i < num_parts; i++) {
__ASSERT(sane_partition_domain(domain,
domain->partitions[i]),
"");
}
#endif
}
sys_dlist_init(&domain->mem_domain_q);
@ -81,7 +133,7 @@ void k_mem_domain_destroy(struct k_mem_domain *domain)
}
void k_mem_domain_add_partition(struct k_mem_domain *domain,
struct k_mem_partition *part)
struct k_mem_partition *part)
{
int p_idx;
unsigned int key;
@ -90,7 +142,9 @@ void k_mem_domain_add_partition(struct k_mem_domain *domain,
__ASSERT(part != NULL, "");
__ASSERT((part->start + part->size) > part->start, "");
ensure_w_xor_x(part->attr);
#if defined(CONFIG_EXECUTE_XOR_WRITE)
__ASSERT(sane_partition_domain(domain, part), "");
#endif
key = irq_lock();