Xander
Xander

Reputation: 428

How to allocate executable memory in Linux

Since the release of the Linux 5.8 kernel the signature of *__vmalloc() has changed from:

void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)

To this

void *__vmalloc(unsigned long size, gfp_t gfp_mask)

Which means you cannot allocate executable memory via this function. So this is what your code will look like after that update:

#if LINUX_VERSION_CODE < KERNEL_VERSION(5,8,0)
return __vmalloc(len, GFP_KERNEL, PAGE_KERNEL_EXEC);
#else
return __vmalloc(len, GFP_KERNEL);
#endif

But this means, that without using the noexec parameter for the kernel, it is not possible to vmalloc executable memory for Kernels >= 5.8.

What is the alternative here, or in other words, how can I still allocate executable memory after kernel 5.8?

Upvotes: 3

Views: 551

Answers (1)

Mathieu
Mathieu

Reputation: 9709

Looking at vmalloc.c, we can see that

  • __vmalloc calls __vmalloc_node which calls __vmalloc_node_range (see below)
  • __vmalloc_node_range has kept the prot parameter.

So, we can imagine recreate __vmalloc(..., PAGE_KERNEL_EXEC); from __vmalloc_node_range:

#if LINUX_VERSION_CODE < KERNEL_VERSION(5,8,0)
return __vmalloc(len, GFP_KERNEL, PAGE_KERNEL_EXEC);
#else
return __vmalloc_node_range(size, 1, VMALLOC_START, VMALLOC_END, gfp_mask,
             PAGE_KERNEL_EXEC, 0, node, caller);
#endif

The extracted code from vmalloc.c:

void *__vmalloc_node(unsigned long size, unsigned long align,
                gfp_t gfp_mask, int node, const void *caller)
{
    return __vmalloc_node_range(size, align, VMALLOC_START, VMALLOC_END,
                gfp_mask, PAGE_KERNEL, 0, node, caller);
}
void *__vmalloc(unsigned long size, gfp_t gfp_mask)
{
    return __vmalloc_node(size, 1, gfp_mask, NUMA_NO_NODE,
                __builtin_return_address(0));
}

Upvotes: 2

Related Questions