The bootloader has a preload feature that allows a kernel module to be
allocated
by the bootloader in low memory before the kernel is started. A pointer to
the
module is given to the kernel via a bootinfo, and the kernel parses it and
uses
it as-is in low memory. There is no granularity provided by this process.
The first patch [1] forces the kernel to reallocate the preloaded modules
into
dynamic memory. We can then map this area [2] read-only on amd64 and i386.
When loading a module from VFS (and now, from the bootloader), the kernel
packs
up the useful data into one big RWX chunk. The second patch [3] splits this
chunk into two different text and data+bss+rodata chunks. The latter is made
non-executable. Note that this also provides some kind of ASLR, since the
chunks
are not necessarily contiguous.
It will be easy then to put rodata into another chunk and mprotect the text
and
rodata so that they get only RX and R. There is still [4], but I'll fix that
later.
Ok?
[1] http://m00nbsd.net/garbage/modules_1.diff
[2] https://nxr.netbsd.org/xref/src/sys/arch/amd64/amd64/locore.S#706
[3] http://m00nbsd.net/garbage/modules_2.diff
[4] https://nxr.netbsd.org/xref/src/sys/gdbscripts/module#70