Linux 6.9-rc1
[linux-2.6-microblaze.git] / mm / memfd.c
index 08f5f83..7d8d3ab 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/hugetlb.h>
 #include <linux/shmem_fs.h>
 #include <linux/memfd.h>
+#include <linux/pid_namespace.h>
 #include <uapi/linux/memfd.h>
 
 /*
 #define MEMFD_TAG_PINNED        PAGECACHE_TAG_TOWRITE
 #define LAST_SCAN               4       /* about 150ms max */
 
+static bool memfd_folio_has_extra_refs(struct folio *folio)
+{
+       return folio_ref_count(folio) - folio_mapcount(folio) !=
+              folio_nr_pages(folio);
+}
+
 static void memfd_tag_pins(struct xa_state *xas)
 {
-       struct page *page;
+       struct folio *folio;
        int latency = 0;
-       int cache_count;
 
        lru_add_drain();
 
        xas_lock_irq(xas);
-       xas_for_each(xas, page, ULONG_MAX) {
-               cache_count = 1;
-               if (!xa_is_value(page) &&
-                   PageTransHuge(page) && !PageHuge(page))
-                       cache_count = HPAGE_PMD_NR;
-
-               if (!xa_is_value(page) &&
-                   page_count(page) - total_mapcount(page) != cache_count)
+       xas_for_each(xas, folio, ULONG_MAX) {
+               if (!xa_is_value(folio) && memfd_folio_has_extra_refs(folio))
                        xas_set_mark(xas, MEMFD_TAG_PINNED);
-               if (cache_count != 1)
-                       xas_set(xas, page->index + cache_count);
 
-               latency += cache_count;
-               if (latency < XA_CHECK_SCHED)
+               if (++latency < XA_CHECK_SCHED)
                        continue;
                latency = 0;
 
@@ -65,16 +62,16 @@ static void memfd_tag_pins(struct xa_state *xas)
 /*
  * Setting SEAL_WRITE requires us to verify there's no pending writer. However,
  * via get_user_pages(), drivers might have some pending I/O without any active
- * user-space mappings (eg., direct-IO, AIO). Therefore, we look at all pages
+ * user-space mappings (eg., direct-IO, AIO). Therefore, we look at all folios
  * and see whether it has an elevated ref-count. If so, we tag them and wait for
  * them to be dropped.
  * The caller must guarantee that no new user will acquire writable references
- * to those pages to avoid races.
+ * to those folios to avoid races.
  */
 static int memfd_wait_for_pins(struct address_space *mapping)
 {
        XA_STATE(xas, &mapping->i_pages, 0);
-       struct page *page;
+       struct folio *folio;
        int error, scan;
 
        memfd_tag_pins(&xas);
@@ -82,7 +79,6 @@ static int memfd_wait_for_pins(struct address_space *mapping)
        error = 0;
        for (scan = 0; scan <= LAST_SCAN; scan++) {
                int latency = 0;
-               int cache_count;
 
                if (!xas_marked(&xas, MEMFD_TAG_PINNED))
                        break;
@@ -94,20 +90,15 @@ static int memfd_wait_for_pins(struct address_space *mapping)
 
                xas_set(&xas, 0);
                xas_lock_irq(&xas);
-               xas_for_each_marked(&xas, page, ULONG_MAX, MEMFD_TAG_PINNED) {
+               xas_for_each_marked(&xas, folio, ULONG_MAX, MEMFD_TAG_PINNED) {
                        bool clear = true;
 
-                       cache_count = 1;
-                       if (!xa_is_value(page) &&
-                           PageTransHuge(page) && !PageHuge(page))
-                               cache_count = HPAGE_PMD_NR;
-
-                       if (!xa_is_value(page) && cache_count !=
-                           page_count(page) - total_mapcount(page)) {
+                       if (!xa_is_value(folio) &&
+                           memfd_folio_has_extra_refs(folio)) {
                                /*
                                 * On the last scan, we clean up all those tags
                                 * we inserted; but make a note that we still
-                                * found pages pinned.
+                                * found folios pinned.
                                 */
                                if (scan == LAST_SCAN)
                                        error = -EBUSY;
@@ -117,8 +108,7 @@ static int memfd_wait_for_pins(struct address_space *mapping)
                        if (clear)
                                xas_clear_mark(&xas, MEMFD_TAG_PINNED);
 
-                       latency += cache_count;
-                       if (latency < XA_CHECK_SCHED)
+                       if (++latency < XA_CHECK_SCHED)
                                continue;
                        latency = 0;
 
@@ -147,6 +137,7 @@ static unsigned int *memfd_file_seals_ptr(struct file *file)
 }
 
 #define F_ALL_SEALS (F_SEAL_SEAL | \
+                    F_SEAL_EXEC | \
                     F_SEAL_SHRINK | \
                     F_SEAL_GROW | \
                     F_SEAL_WRITE | \
@@ -175,6 +166,7 @@ static int memfd_add_seals(struct file *file, unsigned int seals)
         *   SEAL_SHRINK: Prevent the file from shrinking
         *   SEAL_GROW: Prevent the file from growing
         *   SEAL_WRITE: Prevent write access to the file
+        *   SEAL_EXEC: Prevent modification of the exec bits in the file mode
         *
         * As we don't require any trust relationship between two parties, we
         * must prevent seals from being removed. Therefore, sealing a file
@@ -219,6 +211,12 @@ static int memfd_add_seals(struct file *file, unsigned int seals)
                }
        }
 
+       /*
+        * SEAL_EXEC implys SEAL_WRITE, making W^X from the start.
+        */
+       if (seals & F_SEAL_EXEC && inode->i_mode & 0111)
+               seals |= F_SEAL_SHRINK|F_SEAL_GROW|F_SEAL_WRITE|F_SEAL_FUTURE_WRITE;
+
        *file_seals |= seals;
        error = 0;
 
@@ -234,16 +232,12 @@ static int memfd_get_seals(struct file *file)
        return seals ? *seals : -EINVAL;
 }
 
-long memfd_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
+long memfd_fcntl(struct file *file, unsigned int cmd, unsigned int arg)
 {
        long error;
 
        switch (cmd) {
        case F_ADD_SEALS:
-               /* disallow upper 32bit */
-               if (arg > UINT_MAX)
-                       return -EINVAL;
-
                error = memfd_add_seals(file, arg);
                break;
        case F_GET_SEALS:
@@ -261,7 +255,30 @@ long memfd_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
 #define MFD_NAME_PREFIX_LEN (sizeof(MFD_NAME_PREFIX) - 1)
 #define MFD_NAME_MAX_LEN (NAME_MAX - MFD_NAME_PREFIX_LEN)
 
-#define MFD_ALL_FLAGS (MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_HUGETLB)
+#define MFD_ALL_FLAGS (MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_HUGETLB | MFD_NOEXEC_SEAL | MFD_EXEC)
+
+static int check_sysctl_memfd_noexec(unsigned int *flags)
+{
+#ifdef CONFIG_SYSCTL
+       struct pid_namespace *ns = task_active_pid_ns(current);
+       int sysctl = pidns_memfd_noexec_scope(ns);
+
+       if (!(*flags & (MFD_EXEC | MFD_NOEXEC_SEAL))) {
+               if (sysctl >= MEMFD_NOEXEC_SCOPE_NOEXEC_SEAL)
+                       *flags |= MFD_NOEXEC_SEAL;
+               else
+                       *flags |= MFD_EXEC;
+       }
+
+       if (!(*flags & MFD_NOEXEC_SEAL) && sysctl >= MEMFD_NOEXEC_SCOPE_NOEXEC_ENFORCED) {
+               pr_err_ratelimited(
+                       "%s[%d]: memfd_create() requires MFD_NOEXEC_SEAL with vm.memfd_noexec=%d\n",
+                       current->comm, task_pid_nr(current), sysctl);
+               return -EACCES;
+       }
+#endif
+       return 0;
+}
 
 SYSCALL_DEFINE2(memfd_create,
                const char __user *, uname,
@@ -283,6 +300,14 @@ SYSCALL_DEFINE2(memfd_create,
                        return -EINVAL;
        }
 
+       /* Invalid if both EXEC and NOEXEC_SEAL are set.*/
+       if ((flags & MFD_EXEC) && (flags & MFD_NOEXEC_SEAL))
+               return -EINVAL;
+
+       error = check_sysctl_memfd_noexec(&flags);
+       if (error < 0)
+               return error;
+
        /* length includes terminating zero */
        len = strnlen_user(uname, MFD_NAME_MAX_LEN + 1);
        if (len <= 0)
@@ -326,9 +351,20 @@ SYSCALL_DEFINE2(memfd_create,
        file->f_mode |= FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;
        file->f_flags |= O_LARGEFILE;
 
-       if (flags & MFD_ALLOW_SEALING) {
+       if (flags & MFD_NOEXEC_SEAL) {
+               struct inode *inode = file_inode(file);
+
+               inode->i_mode &= ~0111;
+               file_seals = memfd_file_seals_ptr(file);
+               if (file_seals) {
+                       *file_seals &= ~F_SEAL_SEAL;
+                       *file_seals |= F_SEAL_EXEC;
+               }
+       } else if (flags & MFD_ALLOW_SEALING) {
+               /* MFD_EXEC and MFD_ALLOW_SEALING are set */
                file_seals = memfd_file_seals_ptr(file);
-               *file_seals &= ~F_SEAL_SEAL;
+               if (file_seals)
+                       *file_seals &= ~F_SEAL_SEAL;
        }
 
        fd_install(fd, file);