You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2026/10/22 10:40:13 UTC
(nuttx) 06/08: fs/mmap: Implement mprotect().
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 <ma...@gmail.com>
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 <ma...@gmail.com>
---
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. */