This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 07c4a9165d60a549079b9a5d9df39cb80eb1527a Author: Marco Casaroli <[email protected]> AuthorDate: Mon Oct 19 21:34:40 2026 +0200 fs/mmap: Implement mprotect(). mprotect() returned 0 and changed nothing, also in kernel builds where the MMU can change the page permissions. A program that made a page read-only could still write it, and a program could not make a page executable. Bad arguments were not reported either. In all builds, mprotect() now returns EINVAL when addr is not a multiple of the page size that sysconf(_SC_PAGESIZE) reports, or when prot has a bit other than PROT_READ, PROT_WRITE and PROT_EXEC. It returns 0 when len is 0. With CONFIG_ARCH_ADDRENV, mprotect() then calls up_addrenv_mprot() for the address environment of the caller, which returns ENOMEM when a page of the range is not mapped user memory. The mapping list lock is held during the change, so that munmap() of another thread cannot remove the pages meanwhile. When prot has PROT_EXEC, mprotect() also cleans the data cache and invalidates the instruction cache of the range, so that code written to the pages runs without a cache maintenance call in the program. PROT_WRITE | PROT_EXEC is allowed. A W^X policy can come later as an option. Without CONFIG_ARCH_ADDRENV a valid request still returns 0 and changes nothing: without an MMU the permissions cannot be enforced. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- fs/mmap/fs_mmisc.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/fs/mmap/fs_mmisc.c b/fs/mmap/fs_mmisc.c index 658e8cae5a5..b77858315b6 100644 --- a/fs/mmap/fs_mmisc.c +++ b/fs/mmap/fs_mmisc.c @@ -24,7 +24,18 @@ * Included Files ****************************************************************************/ +#include <nuttx/config.h> + #include <sys/mman.h> +#include <errno.h> +#include <unistd.h> + +#include <nuttx/addrenv.h> +#include <nuttx/arch.h> +#include <nuttx/cache.h> +#include <nuttx/mm/map.h> +#include <nuttx/pgalloc.h> +#include <nuttx/sched.h> /**************************************************************************** * Public Functions @@ -52,11 +63,65 @@ int munlockall(void) return 0; } -/* The empty implementation is enough since MMU/MPU mayn't exist. */ +/* Without an address environment the arguments are checked, but there are + * no page permissions to change. + */ int mprotect(FAR void *addr, size_t len, int prot) { - return 0; +#if defined(CONFIG_ARCH_ADDRENV) && defined(CONFIG_MM_PGALLOC) + FAR struct addrenv_s *addrenv = nxsched_self()->addrenv_own; +#endif + uintptr_t start = (uintptr_t)addr; + long pagesize = sysconf(_SC_PAGESIZE); + int ret; + + if ((prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) != 0 || + (pagesize > 0 && start % pagesize != 0)) + { + ret = -EINVAL; + goto errout; + } + + if (len == 0) + { + return OK; + } + +#if defined(CONFIG_ARCH_ADDRENV) && defined(CONFIG_MM_PGALLOC) + len = MM_PGALIGNUP(len); + if (len == 0 || start + len < start || addrenv == NULL) + { + ret = -ENOMEM; + goto errout; + } + + /* Hold the mapping list so that munmap() cannot remove pages meanwhile */ + + ret = mm_map_lock(); + if (ret < 0) + { + goto errout; + } + + ret = up_addrenv_mprot(&addrenv->addrenv, start, len, prot); + mm_map_unlock(); + if (ret < 0) + { + goto errout; + } + + if ((prot & PROT_EXEC) != 0) + { + up_coherent_dcache(start, len); + } +#endif + + return OK; + +errout: + set_errno(-ret); + return ERROR; } /* Ignore the advice since there is no alternative strategy to select. */
