Theo de Raadt <dera...@openbsd.org> wrote:

> Theo de Raadt <dera...@openbsd.org> wrote:
> 
> > In this version of the diff, the kernel manages to mark immutable most of
> > the main binary, and in the shared-binary case, also most of ld.so.  But it
> > cannot mark all of the ELF mapping -- because of two remaining problems 
> > (RELRO
> > in .data, and the malloc.c self-protected bookkeeping page in .bss).  I am
> > looking into various solutions for both of those.

Yet another version of the diff as I incrementally get it working better.
Call it version 22..

Some things of note:

1. Some linkers appear to be creating non-aligned relro sections, which
   has security implications as they cannot be mprotected correctly.  I
   am happy this work has exposed the problem as severe.  I have a
   workaround in ld.so for now that allows these cases to work, and
   later on we can perhaps add a warning to ld.so to identify these
   linkers and get them fixed.

2. But the relro is still not handled perfectly, and I hope someone else's
   eyes can compare addresses and spot what's wrong.

3. ld.so has to cut the list of mutable mappings from the immutable mappings,
   before applying them (late, to satisfy 1).    This is in _dl_apply_mutable().
   After redoing this a couple of times, I am still not proud of it.

4. uvm_unmap_remove() must walk the entries in the region twice.  It cannot
   do unmapping work until it knows the region is completely muteable.  This
   might turn into a performance issue.

5. binutils ld support completely untested, I mainly went in there to fix
   objdump and readelf.

6. It would be nice to hear of a pkg that actually has a problem with this
   change.  I haven't found any yet but don't run many myself.

If anyone wants to debug issues, uncomment the // _dl_printf's in ld.so,
and expect a lot of noise.  Then do something like "ktrace -di program
>& log", and generate a kdump seperately.  It also helps if you test
with programs that don't exit, so you can procmap -a -p $pid.  In the
kdump output, it is important to look for "mprotect -1", because that
provides evidence of the worst (silent) problems...


Index: gnu/llvm/lld/ELF/ScriptParser.cpp
===================================================================
RCS file: /cvs/src/gnu/llvm/lld/ELF/ScriptParser.cpp,v
retrieving revision 1.1.1.4
diff -u -p -u -r1.1.1.4 ScriptParser.cpp
--- gnu/llvm/lld/ELF/ScriptParser.cpp   17 Dec 2021 12:25:02 -0000      1.1.1.4
+++ gnu/llvm/lld/ELF/ScriptParser.cpp   2 Sep 2022 15:23:20 -0000
@@ -1478,6 +1478,7 @@ unsigned ScriptParser::readPhdrType() {
                      .Case("PT_GNU_EH_FRAME", PT_GNU_EH_FRAME)
                      .Case("PT_GNU_STACK", PT_GNU_STACK)
                      .Case("PT_GNU_RELRO", PT_GNU_RELRO)
+                     .Case("PT_OPENBSD_MUTABLE", PT_OPENBSD_MUTABLE)
                      .Case("PT_OPENBSD_RANDOMIZE", PT_OPENBSD_RANDOMIZE)
                      .Case("PT_OPENBSD_WXNEEDED", PT_OPENBSD_WXNEEDED)
                      .Case("PT_OPENBSD_BOOTDATA", PT_OPENBSD_BOOTDATA)
Index: gnu/llvm/lld/ELF/Writer.cpp
===================================================================
RCS file: /cvs/src/gnu/llvm/lld/ELF/Writer.cpp,v
retrieving revision 1.3
diff -u -p -u -r1.3 Writer.cpp
--- gnu/llvm/lld/ELF/Writer.cpp 17 Dec 2021 14:46:47 -0000      1.3
+++ gnu/llvm/lld/ELF/Writer.cpp 2 Sep 2022 21:53:22 -0000
@@ -146,7 +146,7 @@ StringRef elf::getOutputSectionName(cons
        {".text.", ".rodata.", ".data.rel.ro.", ".data.", ".bss.rel.ro.",
         ".bss.", ".init_array.", ".fini_array.", ".ctors.", ".dtors.", 
".tbss.",
         ".gcc_except_table.", ".tdata.", ".ARM.exidx.", ".ARM.extab.",
-        ".openbsd.randomdata."})
+        ".openbsd.randomdata.", ".openbsd.mutable." })
     if (isSectionPrefix(v, s->name))
       return v.drop_back();
 
@@ -2469,6 +2469,12 @@ std::vector<PhdrEntry *> Writer<ELFT>::c
       part.ehFrame->getParent() && part.ehFrameHdr->getParent())
     addHdr(PT_GNU_EH_FRAME, part.ehFrameHdr->getParent()->getPhdrFlags())
         ->add(part.ehFrameHdr->getParent());
+
+  // PT_OPENBSD_MUTABLE is an OpenBSD-specific feature. That makes
+  // the dynamic linker fill the segment with zero data, like bss, but
+  // it can be treated differently.
+  if (OutputSection *cmd = findSection(".openbsd.mutable", partNo))
+    addHdr(PT_OPENBSD_MUTABLE, cmd->getPhdrFlags())->add(cmd);
 
   // PT_OPENBSD_RANDOMIZE is an OpenBSD-specific feature. That makes
   // the dynamic linker fill the segment with random data.
Index: gnu/usr.bin/binutils/bfd/elf.c
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils/bfd/elf.c,v
retrieving revision 1.23
diff -u -p -u -r1.23 elf.c
--- gnu/usr.bin/binutils/bfd/elf.c      13 Jan 2015 20:05:01 -0000      1.23
+++ gnu/usr.bin/binutils/bfd/elf.c      10 Sep 2022 07:06:59 -0000
@@ -969,6 +969,7 @@ _bfd_elf_print_private_bfd_data (bfd *ab
            case PT_GNU_EH_FRAME: pt = "EH_FRAME"; break;
            case PT_GNU_STACK: pt = "STACK"; break;
            case PT_OPENBSD_RANDOMIZE: pt = "OPENBSD_RANDOMIZE"; break;
+           case PT_OPENBSD_MUTABLE: pt = "OPENBSD_MUTABLE"; break;
            default: sprintf (buf, "0x%lx", p->p_type); pt = buf; break;
            }
          fprintf (f, "%8s off    0x", pt);
@@ -2296,6 +2297,10 @@ bfd_section_from_phdr (bfd *abfd, Elf_In
       return _bfd_elf_make_section_from_phdr (abfd, hdr, index,
                                              "openbsd_randomize");
 
+    case PT_OPENBSD_MUTABLE:
+      return _bfd_elf_make_section_from_phdr (abfd, hdr, index,
+                                             "openbsd_mutable");
+
     default:
       /* Check for any processor-specific program segment types.
          If no handler for them, default to making "segment" sections.  */
@@ -3199,7 +3204,7 @@ map_sections_to_segments (bfd *abfd)
   bfd_boolean writable;
   int tls_count = 0;
   asection *first_tls = NULL;
-  asection *dynsec, *eh_frame_hdr, *randomdata;
+  asection *dynsec, *eh_frame_hdr, *randomdata, *mutabledata;
   bfd_size_type amt;
 
   if (elf_tdata (abfd)->segment_map != NULL)
@@ -3544,6 +3549,24 @@ map_sections_to_segments (bfd *abfd)
       pm = &m->next;
     }
 
+  /* If there is a .openbsd.mutable section, throw in a PT_OPENBSD_MUTABLEIZE
+     segment.  */
+  mutabledata = bfd_get_section_by_name (abfd, ".openbsd.mutable");
+  if (mutabledata != NULL && (mutabledata->flags & SEC_LOAD) != 0)
+    {
+      amt = sizeof (struct elf_segment_map);
+      m = bfd_zalloc (abfd, amt);
+      if (m == NULL)
+       goto error_return;
+      m->next = NULL;
+      m->p_type = PT_OPENBSD_MUTABLEIZE;
+      m->count = 1;
+      m->sections[0] = mutabledata->output_section;
+
+      *pm = m;
+      pm = &m->next;
+    }
+
   free (sections);
   sections = NULL;
 
@@ -4166,6 +4189,12 @@ get_program_header_size (bfd *abfd)
   if (bfd_get_section_by_name (abfd, ".openbsd.randomdata") != NULL)
     {
       /* We need a PT_OPENBSD_RANDOMIZE segment.  */
+      ++segs;
+    }
+
+  if (bfd_get_section_by_name (abfd, ".openbsd.mutable") != NULL)
+    {
+      /* We need a PT_OPENBSD_MUTABLE segment.  */
       ++segs;
     }
 
Index: gnu/usr.bin/binutils/binutils/readelf.c
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils/binutils/readelf.c,v
retrieving revision 1.13
diff -u -p -u -r1.13 readelf.c
--- gnu/usr.bin/binutils/binutils/readelf.c     11 Dec 2018 17:09:07 -0000      
1.13
+++ gnu/usr.bin/binutils/binutils/readelf.c     10 Sep 2022 07:06:03 -0000
@@ -2158,6 +2158,7 @@ get_segment_type (unsigned long p_type)
                        return "GNU_EH_FRAME";
     case PT_GNU_STACK: return "STACK";
     case PT_OPENBSD_RANDOMIZE: return "OPENBSD_RANDOMIZE";
+    case PT_OPENBSD_MUTABLE: return "OPENBSD_MUTABLE";
 
     default:
       if ((p_type >= PT_LOPROC) && (p_type <= PT_HIPROC))
Index: gnu/usr.bin/binutils/include/elf/common.h
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils/include/elf/common.h,v
retrieving revision 1.12
diff -u -p -u -r1.12 common.h
--- gnu/usr.bin/binutils/include/elf/common.h   11 Dec 2018 17:09:07 -0000      
1.12
+++ gnu/usr.bin/binutils/include/elf/common.h   10 Sep 2022 07:42:26 -0000
@@ -294,6 +294,7 @@
 #define PT_GNU_STACK   (PT_LOOS + 0x474e551)
 
 #define PT_OPENBSD_RANDOMIZE   0x65a3dbe6
+#define PT_OPENBSD_MUTABLE     0x65a3dbe5
 
 
 /* Program segment permissions, in program header p_flags field.  */
Index: gnu/usr.bin/binutils/ld/ldgram.y
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils/ld/ldgram.y,v
retrieving revision 1.8
diff -u -p -u -r1.8 ldgram.y
--- gnu/usr.bin/binutils/ld/ldgram.y    30 Dec 2013 10:30:00 -0000      1.8
+++ gnu/usr.bin/binutils/ld/ldgram.y    10 Sep 2022 07:05:28 -0000
@@ -1021,6 +1021,8 @@ phdr_type:
                            $$ = exp_intop (0x6474e551);
                          else if (strcmp (s, "PT_OPENBSD_RANDOMIZE") == 0)
                            $$ = exp_intop (0x65a3dbe6);
+                         else if (strcmp (s, "PT_OPENBSD_MUTABLE") == 0)
+                           $$ = exp_intop (0x65a3dbe5);
                          else
                            {
                              einfo (_("\
Index: gnu/usr.bin/binutils-2.17/bfd/elf.c
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/bfd/elf.c,v
retrieving revision 1.18
diff -u -p -u -r1.18 elf.c
--- gnu/usr.bin/binutils-2.17/bfd/elf.c 12 Nov 2021 22:23:40 -0000      1.18
+++ gnu/usr.bin/binutils-2.17/bfd/elf.c 10 Sep 2022 06:53:29 -0000
@@ -1103,6 +1103,7 @@ get_segment_type (unsigned int p_type)
     case PT_OPENBSD_RANDOMIZE: pt = "OPENBSD_RANDOMIZE"; break;
     case PT_OPENBSD_WXNEEDED: pt = "OPENBSD_WXNEEDED"; break;
     case PT_OPENBSD_BOOTDATA: pt = "OPENBSD_BOOTDATA"; break;
+    case PT_OPENBSD_MUTABLE: pt = "OPENBSD_MUTABLE"; break;
     default: pt = NULL; break;
     }
   return pt;
@@ -2645,6 +2646,9 @@ bfd_section_from_phdr (bfd *abfd, Elf_In
       return _bfd_elf_make_section_from_phdr (abfd, hdr, index,
                                              "openbsd_wxneeded");
 
+    case PT_OPENBSD_MUTABLE:
+      return _bfd_elf_make_section_from_phdr (abfd, hdr, index,
+                                             "openbsd_mutable");
     default:
       /* Check for any processor-specific program segment types.  */
       bed = get_elf_backend_data (abfd);
@@ -3649,7 +3653,7 @@ map_sections_to_segments (bfd *abfd)
   bfd_boolean writable;
   int tls_count = 0;
   asection *first_tls = NULL;
-  asection *dynsec, *eh_frame_hdr, *randomdata;
+  asection *dynsec, *eh_frame_hdr, *randomdata, *mutabledata;
   bfd_size_type amt;
 
   if (elf_tdata (abfd)->segment_map != NULL)
@@ -4002,6 +4006,24 @@ map_sections_to_segments (bfd *abfd)
       pm = &m->next;
     }
 
+  /* If there is a .openbsd.mutable section, throw in a PT_OPENBSD_MUTABLE
+     segment.  */
+  mutabledata = bfd_get_section_by_name (abfd, ".openbsd.mutable");
+  if (mutabledata != NULL && (mutabledata->flags & SEC_LOAD) != 0)
+    {
+      amt = sizeof (struct elf_segment_map);
+      m = bfd_zalloc (abfd, amt);
+      if (m == NULL)
+       goto error_return;
+      m->next = NULL;
+      m->p_type = PT_OPENBSD_MUTABLE;
+      m->count = 1;
+      m->sections[0] = mutabledata->output_section;
+
+      *pm = m;
+      pm = &m->next;
+    }
+
   if (elf_tdata (abfd)->relro)
     {
       amt = sizeof (struct elf_segment_map);
@@ -4744,6 +4766,12 @@ get_program_header_size (bfd *abfd)
   if (bfd_get_section_by_name (abfd, ".openbsd.randomdata") != NULL)
     {
       /* We need a PT_OPENBSD_RANDOMIZE segment.  */
+      ++segs;
+    }
+
+  if (bfd_get_section_by_name (abfd, ".openbsd.mutable") != NULL)
+    {
+      /* We need a PT_OPENBSD_MUTABLE segment.  */
       ++segs;
     }
 
Index: gnu/usr.bin/binutils-2.17/binutils/readelf.c
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/binutils/readelf.c,v
retrieving revision 1.25
diff -u -p -u -r1.25 readelf.c
--- gnu/usr.bin/binutils-2.17/binutils/readelf.c        25 Dec 2021 01:25:51 
-0000      1.25
+++ gnu/usr.bin/binutils-2.17/binutils/readelf.c        10 Sep 2022 06:50:24 
-0000
@@ -2708,6 +2708,8 @@ get_segment_type (unsigned long p_type)
                        return "OPENBSD_WXNEEDED";
     case PT_OPENBSD_BOOTDATA:
                        return "OPENBSD_BOOTDATA";
+    case PT_OPENBSD_MUTABLE:
+                       return "OPENBSD_MUTABLE";
 
     default:
       if ((p_type >= PT_LOPROC) && (p_type <= PT_HIPROC))
Index: gnu/usr.bin/binutils-2.17/include/elf/common.h
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/include/elf/common.h,v
retrieving revision 1.14
diff -u -p -u -r1.14 common.h
--- gnu/usr.bin/binutils-2.17/include/elf/common.h      25 Dec 2021 01:25:51 
-0000      1.14
+++ gnu/usr.bin/binutils-2.17/include/elf/common.h      10 Sep 2022 07:41:49 
-0000
@@ -313,6 +313,7 @@
 #define PT_OPENBSD_RANDOMIZE   0x65a3dbe6 /* Fill with random data. */
 #define PT_OPENBSD_WXNEEDED    0x65a3dbe7 /* Program does W^X violations */
 #define PT_OPENBSD_BOOTDATA    0x65a41be6 /* Section for boot arguments */
+#define PT_OPENBSD_MUTABLE     0x65a3dbe5 /* Section for boot arguments */
 
 /* Program segment permissions, in program header p_flags field.  */
 
Index: gnu/usr.bin/binutils-2.17/ld/ldgram.y
===================================================================
RCS file: /cvs/src/gnu/usr.bin/binutils-2.17/ld/ldgram.y,v
retrieving revision 1.6
diff -u -p -u -r1.6 ldgram.y
--- gnu/usr.bin/binutils-2.17/ld/ldgram.y       10 Aug 2016 20:46:08 -0000      
1.6
+++ gnu/usr.bin/binutils-2.17/ld/ldgram.y       10 Sep 2022 07:05:26 -0000
@@ -1097,6 +1097,8 @@ phdr_type:
                            $$ = exp_intop (0x65a3dbe7);
                          else if (strcmp (s, "PT_OPENBSD_BOOTDATA") == 0)
                            $$ = exp_intop (0x65a41be6);
+                         else if (strcmp (s, "PT_OPENBSD_MUTABLE") == 0)
+                           $$ = exp_intop (0x65a3dbe5);
                          else
                            {
                              einfo (_("\
Index: lib/csu/boot.h
===================================================================
RCS file: /cvs/src/lib/csu/boot.h,v
retrieving revision 1.33
diff -u -p -u -r1.33 boot.h
--- lib/csu/boot.h      12 Jan 2022 21:41:06 -0000      1.33
+++ lib/csu/boot.h      10 Sep 2022 10:56:42 -0000
@@ -35,7 +35,6 @@
 #define        _DYN_LOADER
 
 #include <sys/exec_elf.h>
-#include <sys/mman.h>
 
 #include <machine/reloc.h>
 
@@ -50,6 +49,7 @@ void _dl_exit(int);
  */
 #define REDIRECT_SYSCALL(x)    typeof(x) x asm("_libc_"#x) __dso_hidden
 REDIRECT_SYSCALL(mprotect);
+REDIRECT_SYSCALL(mimmutable);
 
 #if RELOC_TAG == DT_RELA
 typedef        Elf_RelA        RELOC_TYPE;
@@ -63,8 +63,10 @@ static void *relro_addr;
 static size_t relro_size;
 #define RCRT0_RELRO()                                                  \
        do {                                                            \
-               if (relro_addr != NULL && relro_size != 0)              \
+               if (relro_addr != NULL && relro_size != 0) {            \
                        mprotect(relro_addr, relro_size, PROT_READ);    \
+                       mimmutable(relro_addr, relro_size);             \
+               }                                                       \
        } while (0)
 
 /*
Index: lib/csu/crt0.c
===================================================================
RCS file: /cvs/src/lib/csu/crt0.c,v
retrieving revision 1.17
diff -u -p -u -r1.17 crt0.c
--- lib/csu/crt0.c      12 Jan 2022 21:41:06 -0000      1.17
+++ lib/csu/crt0.c      3 Sep 2022 14:03:43 -0000
@@ -32,6 +32,7 @@
  */
 
 #include <sys/syscall.h>
+#include <sys/mman.h>
 #include <stdlib.h>
 #include <limits.h>
 
Index: lib/libc/Symbols.list
===================================================================
RCS file: /cvs/src/lib/libc/Symbols.list,v
retrieving revision 1.75
diff -u -p -u -r1.75 Symbols.list
--- lib/libc/Symbols.list       2 Aug 2022 16:45:00 -0000       1.75
+++ lib/libc/Symbols.list       24 Aug 2022 00:39:15 -0000
@@ -131,6 +131,7 @@ _thread_sys_lseek
 _thread_sys_lstat
 _thread_sys_madvise
 _thread_sys_minherit
+_thread_sys_mimmutable
 _thread_sys_mkdir
 _thread_sys_mkdirat
 _thread_sys_mkfifo
@@ -325,6 +326,7 @@ listen
 lseek
 lstat
 madvise
+mimmutable
 minherit
 mkdir
 mkdirat
Index: lib/libc/hidden/sys/mman.h
===================================================================
RCS file: /cvs/src/lib/libc/hidden/sys/mman.h,v
retrieving revision 1.4
diff -u -p -u -r1.4 mman.h
--- lib/libc/hidden/sys/mman.h  11 Jan 2019 18:46:30 -0000      1.4
+++ lib/libc/hidden/sys/mman.h  30 Aug 2022 23:54:47 -0000
@@ -26,6 +26,7 @@ PROTO_NORMAL(mlock);
 PROTO_NORMAL(mlockall);
 PROTO_NORMAL(mmap);
 PROTO_NORMAL(mprotect);
+PROTO_NORMAL(mimmutable);
 PROTO_NORMAL(mquery);
 PROTO_CANCEL(msync);
 PROTO_NORMAL(munlock);
Index: lib/libc/stdlib/malloc.c
===================================================================
RCS file: /cvs/src/lib/libc/stdlib/malloc.c,v
retrieving revision 1.274
diff -u -p -u -r1.274 malloc.c
--- lib/libc/stdlib/malloc.c    30 Jun 2022 17:15:48 -0000      1.274
+++ lib/libc/stdlib/malloc.c    3 Sep 2022 14:01:22 -0000
@@ -228,7 +228,8 @@ struct malloc_readonly {
 static union {
        struct malloc_readonly mopts;
        u_char _pad[MALLOC_PAGESIZE];
-} malloc_readonly __attribute__((aligned(MALLOC_PAGESIZE)));
+} malloc_readonly __attribute__((aligned(MALLOC_PAGESIZE)))
+               __attribute__((section(".openbsd.mutable")));
 #define mopts  malloc_readonly.mopts
 
 char           *malloc_options;        /* compile-time options */
Index: lib/libc/sys/Makefile.inc
===================================================================
RCS file: /cvs/src/lib/libc/sys/Makefile.inc,v
retrieving revision 1.163
diff -u -p -u -r1.163 Makefile.inc
--- lib/libc/sys/Makefile.inc   17 Jul 2022 03:04:27 -0000      1.163
+++ lib/libc/sys/Makefile.inc   24 Aug 2022 00:33:59 -0000
@@ -58,7 +58,7 @@ ASM=  __semctl.o __syscall.o __thrsigdive
        getsockopt.o ioctl.o \
        kevent.o kill.o kqueue.o ktrace.o lchown.o \
        link.o linkat.o listen.o lseek.o lstat.o \
-       madvise.o minherit.o mkdir.o mkdirat.o mkfifo.o mkfifoat.o \
+       madvise.o mimmutable.o minherit.o mkdir.o mkdirat.o mkfifo.o mkfifoat.o 
\
        mknod.o mknodat.o mlock.o mlockall.o mmap.o mount.o mprotect.o \
        mquery.o msgctl.o msgget.o munlock.o munlockall.o munmap.o \
        nfssvc.o \
Index: libexec/ld.so/Makefile
===================================================================
RCS file: /cvs/src/libexec/ld.so/Makefile,v
retrieving revision 1.82
diff -u -p -u -r1.82 Makefile
--- libexec/ld.so/Makefile      23 Dec 2021 18:50:32 -0000      1.82
+++ libexec/ld.so/Makefile      31 Aug 2022 13:49:31 -0000
@@ -28,8 +28,8 @@ SRCS+=        dl_uname.c dl_dirname.c strlcat.c
 SRCS+= malloc.c reallocarray.c tib.c ffs.c
 
 syscall=close exit fstat getdents getentropy getthrid issetugid kbind \
-       mmap mprotect munmap msyscall open pledge read __realpath sendsyslog \
-       __set_tcb sysctl thrkill utrace write
+       mimmutable mmap mprotect munmap msyscall open pledge read \
+       __realpath sendsyslog __set_tcb sysctl thrkill utrace write
 
 .if (${MACHINE_ARCH} == "i386")
 syscall+=mquery
Index: libexec/ld.so/library.c
===================================================================
RCS file: /cvs/src/libexec/ld.so/library.c,v
retrieving revision 1.87
diff -u -p -u -r1.87 library.c
--- libexec/ld.so/library.c     20 Aug 2022 14:11:31 -0000      1.87
+++ libexec/ld.so/library.c     10 Sep 2022 16:35:02 -0000
@@ -95,6 +95,113 @@ unload:
        }
 }
 
+void
+_dl_defer_immutable(elf_object_t *object, void *start, size_t len, char *name)
+{
+       struct mutate *m;
+       int i;
+
+       for (i = 0; i < MAXMUT; i++) {
+               m = &object->imut[i];
+               if (m->valid == 0) {
+//                     _dl_printf("imut\t%x-%x %s\n", start, start + len, 
name);
+                       m->start = (vaddr_t)start;
+                       m->end = (vaddr_t)start + len;
+                       m->name = name;
+                       m->valid = 1;
+                       break;
+               }
+       }
+}
+
+void
+_dl_defer_mutable(elf_object_t *object, void *start, size_t len, char *name)
+{
+       struct mutate *m;
+       int i;
+
+       for (i = 0; i < MAXMUT; i++) {
+               m = &object->mut[i];
+               if (m->valid == 0) {
+//                     _dl_printf(" mut\t%x-%x %s\n", start, start + len, 
name);
+                       m->start = (vaddr_t)start;
+                       m->end = (vaddr_t)start + len;
+                       m->name = name;
+                       m->valid = 1;
+                       break;
+               }
+       }
+}
+
+void
+_dl_apply_mutable(elf_object_t *object, Elf_Addr loff)
+{
+       struct mutate *m, *im, *imtail;
+       int mut, imut;
+       
+       imtail = &object->imut[MAXMUT - 1];
+
+       for (imut = 0; imut < MAXMUT; imut++) {
+               im = &object->imut[imut];
+               if (im->valid == 0)
+                       continue;
+
+               for (mut = 0; mut < MAXMUT; mut++) {
+                       m = &object->mut[mut];
+                       if (m->valid == 0)
+                               continue;
+//                     _dl_printf("try %d-%s %x-%x from %d-%s %x-%x: ",
+//                         mut, m->name, m->start, m->end,
+//                         imut, im->name, im->start, im->end);
+                       if (m->start <= im->start) {
+                               if (m->end < im->start) {
+//                                     _dl_printf(" before ignored");
+                                       ;
+                               } else if (m->end >= im->end) {
+                                       im->start = im->end = im->valid = 0;
+//                                     _dl_printf(" whole: %x-%x", im->start, 
im->end);
+                               } else {
+//                                     _dl_printf(" early: %x-%x", im->start, 
im->end);
+                                       im->start = m->end;
+                               }
+                       } else if (m->start > im->start) {
+                               if (m->end >= im->end) {
+//                                     _dl_printf(" after ignored");
+                                       ;
+                               } else if (m->end < im->end) {
+                                       imtail->start = im->start;
+                                       imtail->end = m->start;
+                                       imtail->valid = 1;
+                                       imtail->name = "split1";
+                                       imtail--;
+                                       imtail->start = m->end;
+                                       imtail->end = im->end;
+                                       imtail->valid = 1;
+                                       imtail->name = "split2";
+                                       imtail--;
+                                       im->start = im->end = im->valid = 0;
+//                                     _dl_printf(" split %x-%x %x-%x",
+//                                         imtail[1].start, imtail[1].end,
+//                                         imtail[2].start, imtail[2].end);
+                               }
+                       }
+//                     _dl_printf("\n");
+               }
+       }
+
+       /* and now, install immutability for objects */
+       for (imut = 0; imut < MAXMUT; imut++) {
+               im = &object->imut[imut];
+               if (im->valid == 0 || (im->start == 0 && im->end == 0))
+                       continue;
+//             _dl_printf("IMUT %lx-%lx %s (%lx,%lx) [%lx,%lx]\n",
+//                 im->start, im->end, object->load_name,
+//                 (void *)im->start + loff, (void *)im->end + loff,
+//                 (void *)im->start + loff, im->end - im->start);
+               _dl_mimmutable((void *)im->start + loff, im->end - im->start);
+       }
+}
+
 elf_object_t *
 _dl_tryload_shlib(const char *libname, int type, int flags)
 {
@@ -212,6 +319,7 @@ _dl_tryload_shlib(const char *libname, i
        phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff);
 
        for (i = 0; i < ehdr->e_phnum; i++, phdp++) {
+
                switch (phdp->p_type) {
                case PT_LOAD: {
                        char *start = (char *)(TRUNC_PG(phdp->p_vaddr)) + loff;
@@ -328,6 +436,40 @@ _dl_tryload_shlib(const char *libname, i
                                _dl_printf("msyscall %lx %lx error\n",
                                    exec_start, exec_size);
                }
+
+               /* Queue all LOAD sections to become immutable, but subtract
+                * the regions which cannot be immutable.  This includes the
+                * specifically marked PT_OPENBSD_MUTABLE, and PT_GNU_RELRO
+                * (which programs mark immutable when protecting later)
+                */
+               if (type == OBJTYPE_LIB &&
+                   ((object->obj_flags & (DF_1_INITFIRST | DF_1_NODELETE)))) {
+                       phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff);
+                       for (i = 0; i < ehdr->e_phnum; i++, phdp++) {
+                               char *  start = (char *)TRUNC_PG(phdp->p_vaddr);
+                               Elf_Addr size = ROUND_PG(phdp->p_memsz);
+
+                               switch (phdp->p_type) {
+                               case PT_LOAD:
+                                       _dl_defer_immutable(object,
+                                           start, size, "LOAD");
+                                       break;
+                               case PT_OPENBSD_RANDOMIZE:
+                                       _dl_defer_immutable(object,
+                                           start, size, "RANDOM");
+                                       break;
+                               case PT_OPENBSD_MUTABLE:
+                                       _dl_defer_mutable(object,
+                                           start, size, "malloc");
+                                       break;
+                               case PT_GNU_RELRO:
+                                       _dl_defer_mutable(object,
+                                           start, size, "relro");
+                                       break;
+                               }
+                       }
+               }
+//             _dl_apply_mutable(object, loff);
        } else {
                _dl_munmap((void *)libaddr, maxva - minva);
                _dl_load_list_free(load_list);
Index: libexec/ld.so/loader.c
===================================================================
RCS file: /cvs/src/libexec/ld.so/loader.c,v
retrieving revision 1.195
diff -u -p -u -r1.195 loader.c
--- libexec/ld.so/loader.c      8 Jan 2022 06:49:41 -0000       1.195
+++ libexec/ld.so/loader.c      10 Sep 2022 16:28:48 -0000
@@ -375,7 +375,7 @@ _dl_load_dep_libs(elf_object_t *object, 
                                DL_DEB(("loading: %s required by %s\n", libname,
                                    dynobj->load_name));
                                depobj = _dl_load_shlib(libname, dynobj,
-                                   OBJTYPE_LIB, depflags);
+                                   OBJTYPE_LIB, depflags | DF_1_NODELETE);
                                if (depobj == 0) {
                                        if (booting) {
                                                _dl_die(
@@ -432,12 +432,13 @@ _dl_self_relro(long loff)
                case PT_GNU_RELRO:
                        _dl_mprotect((void *)(phdp->p_vaddr + loff),
                            phdp->p_memsz, PROT_READ);
+                       _dl_mimmutable((void *)(phdp->p_vaddr + loff),
+                           phdp->p_memsz);
                        break;
                }
        }
 }
 
-
 #define PFLAGS(X) ((((X) & PF_R) ? PROT_READ : 0) | \
                   (((X) & PF_W) ? PROT_WRITE : 0) | \
                   (((X) & PF_X) ? PROT_EXEC : 0))
@@ -785,9 +786,16 @@ _dl_relro(elf_object_t *object)
        if (object->relro_addr != 0 && object->relro_size != 0) {
                Elf_Addr addr = object->relro_addr;
 
-               DL_DEB(("protect RELRO [0x%lx,0x%lx) in %s\n",
-                   addr, addr + object->relro_size, object->load_name));
+//             _dl_printf("protect RELRO [0x%lx,0x%lx) in %s\n",
+//                 addr, addr + object->relro_size, object->load_name);
                _dl_mprotect((void *)addr, object->relro_size, PROT_READ);
+
+               /* if library will never be unloaded, immutable is OK */
+               if ((object->obj_flags & DF_1_NODELETE)) {
+//                     _dl_printf("immutable RELRO %s %p-%p %x\n", 
object->load_name,
+//                         addr, addr + object->relro_size, 
object->relro_size);
+                       _dl_mimmutable((void *)addr, object->relro_size);
+               }
        }
 }
 
@@ -812,8 +820,10 @@ _dl_call_init_recurse(elf_object_t *obje
        if (initfirst && (object->obj_flags & DF_1_INITFIRST) == 0)
                return;
 
-       if (!initfirst)
+       if (!initfirst) {
                _dl_relro(object);
+               _dl_apply_mutable(object, object->obj_base);
+       }
 
        if (object->dyn.init) {
                DL_DEB(("doing ctors obj %p @%p: [%s]\n",
@@ -832,8 +842,10 @@ _dl_call_init_recurse(elf_object_t *obje
                            environ, &_dl_cb_cb);
        }
 
-       if (initfirst)
+       if (initfirst) {
                _dl_relro(object);
+               _dl_apply_mutable(object, object->obj_base);
+       }
 
        object->status |= STAT_INIT_DONE;
 }
@@ -979,4 +991,3 @@ _dl_rreloc(elf_object_t *object)
                }
        }
 }
-
Index: libexec/ld.so/resolve.h
===================================================================
RCS file: /cvs/src/libexec/ld.so/resolve.h,v
retrieving revision 1.101
diff -u -p -u -r1.101 resolve.h
--- libexec/ld.so/resolve.h     20 Aug 2022 14:11:31 -0000      1.101
+++ libexec/ld.so/resolve.h     10 Sep 2022 16:25:23 -0000
@@ -77,6 +77,13 @@ struct object_vector {
 };
 void   object_vec_grow(struct object_vector *_vec, int _more);
 
+struct mutate {
+       vaddr_t start;
+       vaddr_t end;
+       char *name;
+       int valid;
+};
+
 /*
  *  Structure describing a loaded object.
  *  The head of this struct must be compatible
@@ -231,6 +238,10 @@ struct elf_object {
 
        /* nonzero if trace enabled for this object */
        int traced;
+
+#define MAXMUT 20
+       struct mutate imut[MAXMUT];
+       struct mutate mut[MAXMUT];
 };
 
 struct dep_node {
@@ -315,6 +326,8 @@ void _dl_run_all_dtors(void);
 int    _dl_match_file(struct sod *sodp, const char *name, int namelen);
 char   *_dl_find_shlib(struct sod *sodp, char **searchpath, int nohints);
 void   _dl_load_list_free(struct load_list *load_list);
+
+void _dl_apply_mutable(elf_object_t *object, Elf_Addr loff);
 
 typedef void lock_cb(int);
 void   _dl_thread_kern_go(lock_cb *);
Index: libexec/ld.so/sod.c
===================================================================
RCS file: /cvs/src/libexec/ld.so/sod.c,v
retrieving revision 1.36
diff -u -p -u -r1.36 sod.c
--- libexec/ld.so/sod.c 8 Jan 2022 06:49:41 -0000       1.36
+++ libexec/ld.so/sod.c 31 Aug 2022 06:56:02 -0000
@@ -186,6 +186,8 @@ _dl_maphints(void)
        if (hheader->hh_version >= LD_HINTS_VERSION_2)
                _dl_hint_search_path = _dl_split_path(hstrtab + 
hheader->hh_dirlist);
 
+       _dl_mimmutable(addr, hsize);
+
        /* close the file descriptor, leaving the hints mapped */
        _dl_close(hfd);
 
Index: libexec/ld.so/syscall.h
===================================================================
RCS file: /cvs/src/libexec/ld.so/syscall.h,v
retrieving revision 1.2
diff -u -p -u -r1.2 syscall.h
--- libexec/ld.so/syscall.h     8 Jan 2022 06:49:41 -0000       1.2
+++ libexec/ld.so/syscall.h     30 Aug 2022 08:10:24 -0000
@@ -52,6 +52,7 @@ int   _dl_mprotect(const void *, size_t, i
 void   *_dl_mquery(void *, size_t, int, int, int, off_t);
 int    _dl_msyscall(void *addr, size_t len);
 int    _dl_munmap(const void *, size_t);
+int    _dl_mimmutable(const void *, size_t);
 int    _dl_open(const char *, int);
 int    _dl_pledge(const char *, const char **);
 ssize_t        _dl_read(int, const char *, size_t);
Index: libexec/ld.so/aarch64/ld.script
===================================================================
RCS file: /cvs/src/libexec/ld.so/aarch64/ld.script,v
retrieving revision 1.1
diff -u -p -u -r1.1 ld.script
--- libexec/ld.so/aarch64/ld.script     10 May 2019 13:29:21 -0000      1.1
+++ libexec/ld.so/aarch64/ld.script     10 Sep 2022 16:34:00 -0000
@@ -2,6 +2,7 @@ PHDRS
 {
        rodata  PT_LOAD FILEHDR PHDRS FLAGS (4);
        text    PT_LOAD;
+       btext   PT_LOAD FLAGS (0x08000005);
        data    PT_LOAD;
        random  PT_OPENBSD_RANDOMIZE;
        relro   PT_GNU_RELRO;
@@ -28,7 +29,7 @@ SECTIONS
        boot_text_start = .;
        *(.boot.text)
        boot_text_end = .;
-    } :text
+    } :btext
 
     /* RELRO DATA */
     . = DATA_SEGMENT_ALIGN (0x10000, 0x1000);
@@ -62,6 +63,7 @@ SECTIONS
     /* DATA */
     . = ALIGN(0x1000);
     .data      : { *(.data .data.*) } :data
+    . = ALIGN(0x1000);
     .bss       : { *(.dynbss) *(.bss .bss.*) *(COMMON) } :data
     . = DATA_SEGMENT_END (.);
 
Index: libexec/ld.so/amd64/ld.script
===================================================================
RCS file: /cvs/src/libexec/ld.so/amd64/ld.script,v
retrieving revision 1.1
diff -u -p -u -r1.1 ld.script
--- libexec/ld.so/amd64/ld.script       10 May 2019 13:29:21 -0000      1.1
+++ libexec/ld.so/amd64/ld.script       10 Sep 2022 11:33:46 -0000
@@ -2,6 +2,7 @@ PHDRS
 {
        rodata  PT_LOAD FILEHDR PHDRS FLAGS (4);
        text    PT_LOAD;
+       btext   PT_LOAD FLAGS (0x08000005);
        data    PT_LOAD;
        random  PT_OPENBSD_RANDOMIZE;
        relro   PT_GNU_RELRO;
@@ -26,7 +27,7 @@ SECTIONS
        boot_text_start = .;
        *(.boot.text)
        boot_text_end = .;
-    } :text =0xcccccccc
+    } :btext =0xcccccccc
     . = ALIGN(0x1000);
     .text      : { *(.text .text.*) } :text =0xcccccccc
 
@@ -62,6 +63,7 @@ SECTIONS
     /* DATA */
     . = ALIGN(0x1000);
     .data      : { *(.data .data.*) } :data
+    . = ALIGN(0x1000);
     .bss       : { *(.dynbss) *(.bss .bss.*) *(COMMON) } :data
     . = DATA_SEGMENT_END (.);
 
Index: sys/kern/exec_elf.c
===================================================================
RCS file: /cvs/src/sys/kern/exec_elf.c,v
retrieving revision 1.168
diff -u -p -u -r1.168 exec_elf.c
--- sys/kern/exec_elf.c 29 Aug 2022 16:53:46 -0000      1.168
+++ sys/kern/exec_elf.c 10 Sep 2022 06:41:33 -0000
@@ -189,11 +189,16 @@ elf_load_psection(struct exec_vmcmd_set 
         * initially.  The dynamic linker will make these read-only
         * and add back X permission after relocation processing.
         * Static executables with W|X segments will probably crash.
+        * Apply immutability as much as possible, but not for RELRO
+        * or PT_OPENBSD_MUTABLE sections, or LOADS marked entirely
+        * PF_MUTABLE. Userland will take care of those if possible.
         */
        *prot |= (ph->p_flags & PF_R) ? PROT_READ : 0;
        *prot |= (ph->p_flags & PF_W) ? PROT_WRITE : 0;
        if ((ph->p_flags & PF_W) == 0)
                *prot |= (ph->p_flags & PF_X) ? PROT_EXEC : 0;
+       if ((ph->p_flags & PF_MUTABLE) == 0)
+               flags |= VMCMD_IMMUTABLE;
 
        msize = ph->p_memsz + diff;
        offset = ph->p_offset - bdiff;
@@ -432,6 +437,12 @@ elf_load_file(struct proc *p, char *path
                            ph[i].p_memsz, ph[i].p_vaddr + pos, NULLVP, 0, 0);
                        break;
 
+               case PT_GNU_RELRO:
+               case PT_OPENBSD_MUTABLE:
+                       NEW_VMCMD(&epp->ep_vmcmds, vmcmd_mutable,
+                           ph[i].p_memsz, ph[i].p_vaddr + pos, NULLVP, 0, 0);
+                       break;
+
                default:
                        break;
                }
@@ -652,6 +663,12 @@ exec_elf_makecmds(struct proc *p, struct
                        }
                        randomizequota -= ph[i].p_memsz;
                        NEW_VMCMD(&epp->ep_vmcmds, vmcmd_randomize,
+                           ph[i].p_memsz, ph[i].p_vaddr + exe_base, NULLVP, 0, 
0);
+                       break;
+
+               case PT_GNU_RELRO:
+               case PT_OPENBSD_MUTABLE:
+                       NEW_VMCMD(&epp->ep_vmcmds, vmcmd_mutable,
                            ph[i].p_memsz, ph[i].p_vaddr + exe_base, NULLVP, 0, 
0);
                        break;
 
Index: sys/kern/exec_subr.c
===================================================================
RCS file: /cvs/src/sys/kern/exec_subr.c,v
retrieving revision 1.57
diff -u -p -u -r1.57 exec_subr.c
--- sys/kern/exec_subr.c        29 Nov 2019 06:34:45 -0000      1.57
+++ sys/kern/exec_subr.c        3 Sep 2022 13:51:09 -0000
@@ -211,6 +211,10 @@ vmcmd_map_pagedvn(struct proc *p, struct
                 * error: detach from object
                 */
                uobj->pgops->pgo_detach(uobj);
+       } else {
+               if (cmd->ev_flags & VMCMD_IMMUTABLE)
+                       uvm_map_immutable(&p->p_vmspace->vm_map, cmd->ev_addr,
+                           cmd->ev_addr + cmd->ev_len, 1, "pagedvm");
        }
 
        return (error);
@@ -281,6 +285,23 @@ vmcmd_map_zero(struct proc *p, struct ex
            UVM_MAPFLAG(cmd->ev_prot, PROT_MASK, MAP_INHERIT_COPY,
            MADV_NORMAL, UVM_FLAG_FIXED|UVM_FLAG_COPYONW |
            (cmd->ev_flags & VMCMD_STACK ? UVM_FLAG_STACK : 0))));
+}
+
+/*
+ * vmcmd_mutable():
+ *     handle vmcmd which changes an address space region.back to mutable
+ */
+
+int
+vmcmd_mutable(struct proc *p, struct exec_vmcmd *cmd)
+{
+       if (cmd->ev_len == 0)
+               return (0);
+       
+       cmd->ev_addr = trunc_page(cmd->ev_addr);
+       uvm_map_immutable(&p->p_vmspace->vm_map, cmd->ev_addr,
+           cmd->ev_addr + round_page(cmd->ev_len), 0, "mutable");
+       return 0;
 }
 
 /*
Index: sys/kern/init_sysent.c
===================================================================
RCS file: /cvs/src/sys/kern/init_sysent.c,v
retrieving revision 1.249
diff -u -p -u -r1.249 init_sysent.c
--- sys/kern/init_sysent.c      3 Sep 2022 21:16:51 -0000       1.249
+++ sys/kern/init_sysent.c      10 Sep 2022 05:37:44 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: init_sysent.c,v 1.249 2022/09/03 21:16:51 mbuhl Exp $ */
+/*     $OpenBSD$       */
 
 /*
  * System call switch table.
@@ -355,8 +355,8 @@ const struct sysent sysent[] = {
            sys_nosys },                        /* 157 = obsolete statfs25 */
        { 0, 0, 0,
            sys_nosys },                        /* 158 = obsolete fstatfs25 */
-       { 0, 0, 0,
-           sys_nosys },                        /* 159 = unimplemented */
+       { 2, s(struct sys_mimmutable_args), 0,
+           sys_mimmutable },                   /* 159 = mimmutable */
        { 0, 0, 0,
            sys_nosys },                        /* 160 = unimplemented */
        { 2, s(struct sys_getfh_args), 0,
Index: sys/kern/kern_exec.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_exec.c,v
retrieving revision 1.231
diff -u -p -u -r1.231 kern_exec.c
--- sys/kern/kern_exec.c        14 Aug 2022 01:58:27 -0000      1.231
+++ sys/kern/kern_exec.c        3 Sep 2022 06:45:22 -0000
@@ -863,6 +863,8 @@ exec_sigcode_map(struct process *pr)
                uao_detach(sigobject);
                return (ENOMEM);
        }
+       uvm_map_immutable(&pr->ps_vmspace->vm_map, pr->ps_sigcode,
+           pr->ps_sigcode + round_page(sz), 1, "sig");
 
        /* Calculate PC at point of sigreturn entry */
        pr->ps_sigcoderet = pr->ps_sigcode + (sigcoderet - sigcode);
@@ -911,6 +913,8 @@ exec_timekeep_map(struct process *pr)
                uao_detach(timekeep_object);
                return (ENOMEM);
        }
+       uvm_map_immutable(&pr->ps_vmspace->vm_map, pr->ps_timekeep,
+           pr->ps_timekeep + timekeep_sz, 1, "time");
 
        return (0);
 }
Index: sys/kern/kern_pledge.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_pledge.c,v
retrieving revision 1.295
diff -u -p -u -r1.295 kern_pledge.c
--- sys/kern/kern_pledge.c      5 Sep 2022 16:37:47 -0000       1.295
+++ sys/kern/kern_pledge.c      9 Sep 2022 12:56:14 -0000
@@ -150,6 +150,7 @@ const uint64_t pledge_syscalls[SYS_MAXSY
        [SYS_minherit] = PLEDGE_STDIO,
        [SYS_mmap] = PLEDGE_STDIO,
        [SYS_mprotect] = PLEDGE_STDIO,
+       [SYS_mimmutable] = PLEDGE_STDIO,
        [SYS_mquery] = PLEDGE_STDIO,
        [SYS_munmap] = PLEDGE_STDIO,
        [SYS_msync] = PLEDGE_STDIO,
Index: sys/kern/syscalls.c
===================================================================
RCS file: /cvs/src/sys/kern/syscalls.c,v
retrieving revision 1.247
diff -u -p -u -r1.247 syscalls.c
--- sys/kern/syscalls.c 3 Sep 2022 21:16:51 -0000       1.247
+++ sys/kern/syscalls.c 10 Sep 2022 05:37:44 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: syscalls.c,v 1.247 2022/09/03 21:16:51 mbuhl Exp $    */
+/*     $OpenBSD$       */
 
 /*
  * System call names.
@@ -183,7 +183,7 @@ const char *const syscallnames[] = {
        "#156 (obsolete ogetdirentries)",               /* 156 = obsolete 
ogetdirentries */
        "#157 (obsolete statfs25)",             /* 157 = obsolete statfs25 */
        "#158 (obsolete fstatfs25)",            /* 158 = obsolete fstatfs25 */
-       "#159 (unimplemented)",         /* 159 = unimplemented */
+       "mimmutable",                   /* 159 = mimmutable */
        "#160 (unimplemented)",         /* 160 = unimplemented */
        "getfh",                        /* 161 = getfh */
        "#162 (obsolete ogetdomainname)",               /* 162 = obsolete 
ogetdomainname */
Index: sys/kern/syscalls.master
===================================================================
RCS file: /cvs/src/sys/kern/syscalls.master,v
retrieving revision 1.232
diff -u -p -u -r1.232 syscalls.master
--- sys/kern/syscalls.master    3 Sep 2022 21:13:48 -0000       1.232
+++ sys/kern/syscalls.master    10 Sep 2022 05:37:42 -0000
@@ -307,7 +307,7 @@
 156    OBSOL           ogetdirentries
 157    OBSOL           statfs25
 158    OBSOL           fstatfs25
-159    UNIMPL
+159    STD             { int sys_mimmutable(void *addr, size_t len); }
 160    UNIMPL
 161    STD             { int sys_getfh(const char *fname, fhandle_t *fhp); }
 162    OBSOL           ogetdomainname
Index: sys/sys/exec.h
===================================================================
RCS file: /cvs/src/sys/sys/exec.h,v
retrieving revision 1.48
diff -u -p -u -r1.48 exec.h
--- sys/sys/exec.h      1 Sep 2022 07:26:56 -0000       1.48
+++ sys/sys/exec.h      2 Sep 2022 20:06:23 -0000
@@ -93,6 +93,7 @@ struct exec_vmcmd {
 #define VMCMD_BASE      0x0002  /* marks a base entry */
 #define VMCMD_STACK     0x0004  /* create with UVM_FLAG_STACK */
 #define VMCMD_SYSCALL   0x0008  /* create with UVM_FLAG_SYSCALL */
+#define VMCMD_IMMUTABLE        0x0010  /* make immutable */
 };
 
 #define        EXEC_DEFAULT_VMCMD_SETSIZE      8       /* # of cmds in set to 
start */
@@ -148,6 +149,7 @@ int vmcmd_map_pagedvn(struct proc *, str
 int    vmcmd_map_readvn(struct proc *, struct exec_vmcmd *);
 int    vmcmd_map_zero(struct proc *, struct exec_vmcmd *);
 int    vmcmd_randomize(struct proc *, struct exec_vmcmd *);
+int    vmcmd_mutable(struct proc *, struct exec_vmcmd *);
 int    copyargs(struct exec_package *, struct ps_strings *, void *, void *);
 void   setregs(struct proc *, struct exec_package *, u_long, register_t *);
 int    check_exec(struct proc *, struct exec_package *);
Index: sys/sys/exec_elf.h
===================================================================
RCS file: /cvs/src/sys/sys/exec_elf.h,v
retrieving revision 1.94
diff -u -p -u -r1.94 exec_elf.h
--- sys/sys/exec_elf.h  25 Dec 2021 01:25:51 -0000      1.94
+++ sys/sys/exec_elf.h  2 Sep 2022 09:13:17 -0000
@@ -305,6 +305,7 @@ typedef struct {
 #define ELF_SYMTAB      ".symtab"      /* symbol table */
 #define ELF_TEXT        ".text"                /* code */
 #define ELF_OPENBSDRANDOMDATA ".openbsd.randomdata" /* constant randomdata */
+#define ELF_OPENBSDMUTABLE ".openbsd.mutable" /* mutable bss */
 
 
 /* Section Attribute Flags - sh_flags */
@@ -476,6 +477,7 @@ typedef struct {
 #define PT_GNU_EH_FRAME                0x6474e550      /* Exception handling 
info */
 #define PT_GNU_RELRO           0x6474e552      /* Read-only after relocation */
 
+#define PT_OPENBSD_MUTABLE     0x65a3dbe5      /* like bss, but not immutable 
*/
 #define PT_OPENBSD_RANDOMIZE   0x65a3dbe6      /* fill with random data */
 #define PT_OPENBSD_WXNEEDED    0x65a3dbe7      /* program performs W^X 
violations */
 #define PT_OPENBSD_BOOTDATA    0x65a41be6      /* section for boot arguments */
@@ -484,6 +486,7 @@ typedef struct {
 #define PF_X           0x1             /* Executable */
 #define PF_W           0x2             /* Writable */
 #define PF_R           0x4             /* Readable */
+#define PF_MUTABLE     0x08000000      /* Mutable */
 #define PF_MASKPROC    0xf0000000      /* reserved bits for processor */
                                        /*  specific segment flags */
 
Index: sys/sys/mman.h
===================================================================
RCS file: /cvs/src/sys/sys/mman.h,v
retrieving revision 1.34
diff -u -p -u -r1.34 mman.h
--- sys/sys/mman.h      1 Mar 2019 01:46:18 -0000       1.34
+++ sys/sys/mman.h      31 Aug 2022 05:11:09 -0000
@@ -154,6 +154,7 @@ int munlockall(void);
 #if __BSD_VISIBLE
 int    madvise(void *, size_t, int);
 int    minherit(void *, size_t, int);
+int    mimmutable(void *, size_t);
 void * mquery(void *, size_t, int, int, int, off_t);
 #endif
 int    posix_madvise(void *, size_t, int);
Index: sys/sys/syscall.h
===================================================================
RCS file: /cvs/src/sys/sys/syscall.h,v
retrieving revision 1.246
diff -u -p -u -r1.246 syscall.h
--- sys/sys/syscall.h   3 Sep 2022 21:16:51 -0000       1.246
+++ sys/sys/syscall.h   10 Sep 2022 05:37:44 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: syscall.h,v 1.246 2022/09/03 21:16:51 mbuhl Exp $     */
+/*     $OpenBSD$       */
 
 /*
  * System call numbers.
@@ -441,6 +441,9 @@
                                /* 156 is obsolete ogetdirentries */
                                /* 157 is obsolete statfs25 */
                                /* 158 is obsolete fstatfs25 */
+/* syscall: "mimmutable" ret: "int" args: "void *" "size_t" */
+#define        SYS_mimmutable  159
+
 /* syscall: "getfh" ret: "int" args: "const char *" "fhandle_t *" */
 #define        SYS_getfh       161
 
Index: sys/sys/syscallargs.h
===================================================================
RCS file: /cvs/src/sys/sys/syscallargs.h,v
retrieving revision 1.249
diff -u -p -u -r1.249 syscallargs.h
--- sys/sys/syscallargs.h       3 Sep 2022 21:16:51 -0000       1.249
+++ sys/sys/syscallargs.h       10 Sep 2022 05:37:44 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: syscallargs.h,v 1.249 2022/09/03 21:16:51 mbuhl Exp $ */
+/*     $OpenBSD$       */
 
 /*
  * System call argument lists.
@@ -733,6 +733,11 @@ struct sys_nfssvc_args {
        syscallarg(void *) argp;
 };
 
+struct sys_mimmutable_args {
+       syscallarg(void *) addr;
+       syscallarg(size_t) len;
+};
+
 struct sys_getfh_args {
        syscallarg(const char *) fname;
        syscallarg(fhandle_t *) fhp;
@@ -1345,6 +1350,7 @@ int       sys_ypconnect(struct proc *, void *,
 int    sys_nfssvc(struct proc *, void *, register_t *);
 #else
 #endif
+int    sys_mimmutable(struct proc *, void *, register_t *);
 int    sys_getfh(struct proc *, void *, register_t *);
 int    sys___tmpfd(struct proc *, void *, register_t *);
 int    sys_sysarch(struct proc *, void *, register_t *);
Index: sys/uvm/uvm.h
===================================================================
RCS file: /cvs/src/sys/uvm/uvm.h,v
retrieving revision 1.69
diff -u -p -u -r1.69 uvm.h
--- sys/uvm/uvm.h       4 May 2022 14:58:26 -0000       1.69
+++ sys/uvm/uvm.h       24 Aug 2022 00:58:32 -0000
@@ -81,8 +81,6 @@ struct uvm {
 
 /*
  * vm_map_entry etype bits:
- *
- * keep in sync with KVM_ET_*
  */
 #define UVM_ET_OBJ             0x0001  /* it is a uvm_object */
 #define UVM_ET_SUBMAP          0x0002  /* it is a vm_map submap */
@@ -94,6 +92,7 @@ struct uvm {
 #define UVM_ET_WC              0x0080  /* write combining */
 #define UVM_ET_CONCEAL         0x0100  /* omit from dumps */
 #define UVM_ET_SYSCALL         0x0200  /* syscall text segment */
+#define UVM_ET_IMMUTABLE       0x0400  /* entry may not be changed */
 #define UVM_ET_FREEMAPPED      0x8000  /* map entry is on free list (DEBUG) */
 
 #define UVM_ET_ISOBJ(E)                (((E)->etype & UVM_ET_OBJ) != 0)
Index: sys/uvm/uvm_io.c
===================================================================
RCS file: /cvs/src/sys/uvm/uvm_io.c,v
retrieving revision 1.29
diff -u -p -u -r1.29 uvm_io.c
--- sys/uvm/uvm_io.c    12 Mar 2022 08:11:07 -0000      1.29
+++ sys/uvm/uvm_io.c    24 Aug 2022 00:32:40 -0000
@@ -127,7 +127,7 @@ uvm_io(vm_map_t map, struct uio *uio, in
                vm_map_lock(kernel_map);
                TAILQ_INIT(&dead_entries);
                uvm_unmap_remove(kernel_map, kva, kva+chunksz,
-                   &dead_entries, FALSE, TRUE);
+                   &dead_entries, FALSE, TRUE, FALSE);
                vm_map_unlock(kernel_map);
                uvm_unmap_detach(&dead_entries, AMAP_REFALL);
 
Index: sys/uvm/uvm_map.c
===================================================================
RCS file: /cvs/src/sys/uvm/uvm_map.c,v
retrieving revision 1.294
diff -u -p -u -r1.294 uvm_map.c
--- sys/uvm/uvm_map.c   15 Aug 2022 15:53:45 -0000      1.294
+++ sys/uvm/uvm_map.c   10 Sep 2022 16:27:19 -0000
@@ -797,7 +797,13 @@ uvm_mapanon(struct vm_map *map, vaddr_t 
                                error = EINVAL;
                                goto unlock;
                        }
-                       uvm_unmap_remove(map, *addr, *addr + sz, &dead, FALSE, 
TRUE);
+                       if (uvm_unmap_remove(map, *addr, *addr + sz, &dead,
+                           FALSE, TRUE, TRUE) != 0) {
+                               printf("uvm_mapanon: %s\n", 
curproc->p_p->ps_comm);
+                               // XXX immutable must fail
+                               error = ENOMEM;
+                               goto unlock;
+                       }
                }
                if (!uvm_map_isavail(map, NULL, &first, &last, *addr, sz)) {
                        error = ENOMEM;
@@ -1038,8 +1044,15 @@ uvm_map(struct vm_map *map, vaddr_t *add
                }
 
                /* Check that the space is available. */
-               if (flags & UVM_FLAG_UNMAP)
-                       uvm_unmap_remove(map, *addr, *addr + sz, &dead, FALSE, 
TRUE);
+               if (flags & UVM_FLAG_UNMAP) {
+                       if (uvm_unmap_remove(map, *addr, *addr + sz, &dead,
+                           FALSE, TRUE, TRUE) != 0) {
+                               printf("uvm_map: %s\n", curproc->p_p->ps_comm);
+                               // XXX immutable must fail
+                               error = ENOMEM;
+                               goto unlock;
+                       }
+               }
                if (!uvm_map_isavail(map, NULL, &first, &last, *addr, sz)) {
                        error = ENOMEM;
                        goto unlock;
@@ -1817,7 +1830,7 @@ uvm_unmap(struct vm_map *map, vaddr_t st
            (end & (vaddr_t)PAGE_MASK) == 0);
        TAILQ_INIT(&dead);
        vm_map_lock(map);
-       uvm_unmap_remove(map, start, end, &dead, FALSE, TRUE);
+       uvm_unmap_remove(map, start, end, &dead, FALSE, TRUE, FALSE);
        vm_map_unlock(map);
 
        if (map->flags & VM_MAP_INTRSAFE)
@@ -1959,17 +1972,17 @@ uvm_unmap_kill_entry(struct vm_map *map,
  * If markfree, entry will be properly marked free, otherwise, no replacement
  * entry will be put in the tree (corrupting the tree).
  */
-void
+int
 uvm_unmap_remove(struct vm_map *map, vaddr_t start, vaddr_t end,
     struct uvm_map_deadq *dead, boolean_t remove_holes,
-    boolean_t markfree)
+    boolean_t markfree, boolean_t checkimmutable)
 {
        struct vm_map_entry *prev_hint, *next, *entry;
 
        start = MAX(start, map->min_offset);
        end = MIN(end, map->max_offset);
        if (start >= end)
-               return;
+               return 0;
 
        if ((map->flags & VM_MAP_INTRSAFE) == 0)
                splassert(IPL_NONE);
@@ -1984,6 +1997,23 @@ uvm_unmap_remove(struct vm_map *map, vad
        else
                UVM_MAP_CLIP_START(map, entry, start);
 
+       if (checkimmutable) {
+               struct vm_map_entry *entry1 = entry;
+
+               /* Refuse to unmap if any entries are immutable */
+               for (; entry1 != NULL && entry1->start < end; entry1 = next) {
+                       KDASSERT(entry1->start >= start);
+                       if (entry1->end > end || !markfree)
+                               UVM_MAP_CLIP_END(map, entry1, end);
+                       KDASSERT(entry1->start >= start && entry1->end <= end);
+                       next = RBT_NEXT(uvm_map_addr, entry1);
+                       if (entry1->etype & UVM_ET_IMMUTABLE) {
+                               printf("uvm_unmap: %s\n", 
curproc->p_p->ps_comm);
+                               return EPERM;
+                       }
+               }
+       }
+
        /*
         * Iterate entries until we reach end address.
         * prev_hint hints where the freed space can be appended to.
@@ -2043,6 +2073,7 @@ uvm_unmap_remove(struct vm_map *map, vad
                        KDASSERT(uvm_map_entrybyaddr(&map->addr, a) == NULL);
        }
 #endif
+       return 0;
 }
 
 /*
@@ -3098,6 +3129,13 @@ uvm_map_protect(struct vm_map *map, vadd
                if (iter->start == iter->end || UVM_ET_ISHOLE(iter))
                        continue;
 
+               if ((iter->etype & UVM_ET_IMMUTABLE)) {
+                       printf("%s failing mprotect %lx-%lx inside %lx-%lx\n",
+                           curproc->p_p->ps_comm,
+                           iter->start, iter->end, start, end);
+                       error = EPERM;
+                       goto out;
+               }
                old_prot = iter->protection;
                if (old_prot == PROT_NONE && new_prot != old_prot) {
                        dused += uvmspace_dused(
@@ -3356,7 +3394,7 @@ uvmspace_exec(struct proc *p, vaddr_t st
                 * (as in, not replace them with free-memory entries).
                 */
                uvm_unmap_remove(map, map->min_offset, map->max_offset,
-                   &dead_entries, TRUE, FALSE);
+                   &dead_entries, TRUE, FALSE, FALSE);
 
                KDASSERT(RBT_EMPTY(uvm_map_addr, &map->addr));
 
@@ -3529,7 +3567,7 @@ uvm_share(struct vm_map *dstmap, vaddr_t
        }
 
        ret = EINVAL;
-       uvm_unmap_remove(dstmap, dstaddr, unmap_end, &dead, FALSE, TRUE);
+       uvm_unmap_remove(dstmap, dstaddr, unmap_end, &dead, FALSE, TRUE, FALSE);
 
 exit_unlock:
        vm_map_unlock_read(srcmap);
@@ -4088,7 +4126,7 @@ uvm_map_deallocate(vm_map_t map)
        TAILQ_INIT(&dead);
        uvm_tree_sanity(map, __FILE__, __LINE__);
        uvm_unmap_remove(map, map->min_offset, map->max_offset, &dead,
-           TRUE, FALSE);
+           TRUE, FALSE, FALSE);
        pmap_destroy(map->pmap);
        KASSERT(RBT_EMPTY(uvm_map_addr, &map->addr));
        free(map, M_VMMAP, sizeof *map);
@@ -4183,6 +4221,52 @@ uvm_map_syscall(struct vm_map *map, vadd
        return (0);
 }
 
+/* 
+ * uvm_map_immutable: block mapping/mprotect for range of addrs in map.
+ *
+ * => map must be unlocked
+ */
+int
+uvm_map_immutable(struct vm_map *map, vaddr_t start, vaddr_t end, int imut, 
char *name)
+{
+       struct vm_map_entry *entry;
+
+       if (start > end)
+               return EINVAL;
+       start = MAX(start, map->min_offset);
+       end = MIN(end, map->max_offset);
+       if (start >= end)
+               return 0;
+
+       vm_map_lock(map);
+
+       entry = uvm_map_entrybyaddr(&map->addr, start);
+       if (entry->end > start)
+               UVM_MAP_CLIP_START(map, entry, start);
+       else
+               entry = RBT_NEXT(uvm_map_addr, entry);
+
+       while (entry != NULL && entry->start < end) {
+               UVM_MAP_CLIP_END(map, entry, end);
+               if (imut) {     
+//                     printf("%s %lx imut\n", name, entry->start);
+                       if ((entry->etype & UVM_ET_IMMUTABLE))
+//                             printf("%s already immutable %lx-%lx inside 
%lx-%lx\n",
+//                                 curproc->p_p->ps_comm,
+//                                 entry->start, entry->end, start, end);
+                       entry->etype |= UVM_ET_IMMUTABLE;
+               } else {
+//                     printf("%s %lx-%lx mut\n", name, entry->start, 
entry->end);
+                       entry->etype &= ~UVM_ET_IMMUTABLE;
+               }
+               entry = RBT_NEXT(uvm_map_addr, entry);
+       }
+
+       map->wserial++;
+       vm_map_unlock(map);
+       return (0);
+}
+
 /*
  * uvm_map_advice: set advice code for range of addrs in map.
  *
@@ -4367,7 +4451,7 @@ uvm_map_extract(struct vm_map *srcmap, v
 fail2_unmap:
        if (error) {
                uvm_unmap_remove(kernel_map, dstaddr, dstaddr + len, &dead,
-                   FALSE, TRUE);
+                   FALSE, TRUE, FALSE);
        }
 
        /* Release maps, release dead entries. */
Index: sys/uvm/uvm_map.h
===================================================================
RCS file: /cvs/src/sys/uvm/uvm_map.h,v
retrieving revision 1.75
diff -u -p -u -r1.75 uvm_map.h
--- sys/uvm/uvm_map.h   12 Mar 2022 08:11:07 -0000      1.75
+++ sys/uvm/uvm_map.h   3 Sep 2022 06:46:38 -0000
@@ -350,6 +350,7 @@ struct vm_map *     uvm_map_create(pmap_t, v
 vaddr_t                uvm_map_pie(vaddr_t);
 vaddr_t                uvm_map_hint(struct vmspace *, vm_prot_t, vaddr_t, 
vaddr_t);
 int            uvm_map_syscall(struct vm_map *, vaddr_t, vaddr_t);
+int            uvm_map_immutable(struct vm_map *, vaddr_t, vaddr_t, int, char 
*);
 int            uvm_map_inherit(struct vm_map *, vaddr_t, vaddr_t, 
vm_inherit_t);
 int            uvm_map_advice(struct vm_map *, vaddr_t, vaddr_t, int);
 void           uvm_map_init(void);
@@ -365,8 +366,8 @@ int         uvm_map_submap(struct vm_map *, vad
                    struct vm_map *);
 void           uvm_unmap(struct vm_map *, vaddr_t, vaddr_t);
 void           uvm_unmap_detach(struct uvm_map_deadq *, int);
-void           uvm_unmap_remove(struct vm_map*, vaddr_t, vaddr_t,
-                   struct uvm_map_deadq *, boolean_t, boolean_t);
+int            uvm_unmap_remove(struct vm_map*, vaddr_t, vaddr_t,
+                   struct uvm_map_deadq *, boolean_t, boolean_t, boolean_t);
 void           uvm_map_set_uaddr(struct vm_map*, struct uvm_addr_state**,
                    struct uvm_addr_state*);
 int            uvm_map_mquery(struct vm_map*, vaddr_t*, vsize_t, voff_t, int);
Index: sys/uvm/uvm_mmap.c
===================================================================
RCS file: /cvs/src/sys/uvm/uvm_mmap.c,v
retrieving revision 1.172
diff -u -p -u -r1.172 uvm_mmap.c
--- sys/uvm/uvm_mmap.c  1 Aug 2022 14:56:59 -0000       1.172
+++ sys/uvm/uvm_mmap.c  3 Sep 2022 06:49:27 -0000
@@ -569,7 +569,11 @@ sys_munmap(struct proc *p, void *v, regi
        }
 
        TAILQ_INIT(&dead_entries);
-       uvm_unmap_remove(map, addr, addr + size, &dead_entries, FALSE, TRUE);
+       if (uvm_unmap_remove(map, addr, addr + size, &dead_entries,
+           FALSE, TRUE, TRUE) != 0) {
+               vm_map_unlock(map);
+               return EPERM;
+       }
        vm_map_unlock(map);     /* and unlock */
 
        uvm_unmap_detach(&dead_entries, 0);
@@ -649,6 +653,32 @@ sys_msyscall(struct proc *p, void *v, re
 }
 
 /*
+ * sys_mimmutable: the mimmutable system call
+ */
+int
+sys_mimmutable(struct proc *p, void *v, register_t *retval)
+{
+       struct sys_mimmutable_args /* {
+               immutablearg(void *) addr;
+               immutablearg(size_t) len;
+       } */ *uap = v;
+       vaddr_t addr;
+       vsize_t size, pageoff;
+
+       addr = (vaddr_t)SCARG(uap, addr);
+       size = (vsize_t)SCARG(uap, len);
+
+       /*
+        * align the address to a page boundary, and adjust the size accordingly
+        */
+       ALIGN_ADDR(addr, size, pageoff);
+       if (addr > SIZE_MAX - size)
+               return EINVAL;          /* disallow wrap-around. */
+
+       return uvm_map_immutable(&p->p_vmspace->vm_map, addr, addr+size, 1, 
"sys");
+}
+
+/*
  * sys_minherit: the minherit system call
  */
 int
@@ -1228,7 +1258,8 @@ redo:
                        if (kva != 0) {
                                vm_map_lock(kernel_map);
                                uvm_unmap_remove(kernel_map, kva,
-                                   kva+PAGE_SIZE, &dead_entries, FALSE, TRUE);
+                                   kva+PAGE_SIZE, &dead_entries,
+                                   FALSE, TRUE, FALSE);        /* XXX */
                                vm_map_unlock(kernel_map);
                                kva = 0;
                        }
@@ -1255,7 +1286,7 @@ redo:
        if (kva != 0) {
                vm_map_lock(kernel_map);
                uvm_unmap_remove(kernel_map, kva, kva+PAGE_SIZE,
-                   &dead_entries, FALSE, TRUE);
+                   &dead_entries, FALSE, TRUE, FALSE);         /* XXX */
                vm_map_unlock(kernel_map);
        }
        uvm_unmap_detach(&dead_entries, AMAP_REFALL);
Index: usr.sbin/procmap/procmap.c
===================================================================
RCS file: /cvs/src/usr.sbin/procmap/procmap.c,v
retrieving revision 1.69
diff -u -p -u -r1.69 procmap.c
--- usr.sbin/procmap/procmap.c  22 Feb 2022 17:35:01 -0000      1.69
+++ usr.sbin/procmap/procmap.c  24 Aug 2022 02:09:17 -0000
@@ -497,7 +497,7 @@ process_map(kvm_t *kd, pid_t pid, struct
                    (int)sizeof(int) * 2 - 1,  "Size ");
 #endif
        if (print_all)
-               printf("%-*s %-*s %*s %-*s rwxSepc  RWX  I/W/A Dev  %*s - 
File\n",
+               printf("%-*s %-*s %*s %-*s rwxSeIpc  RWX  I/W/A Dev  %*s - 
File\n",
                    (int)sizeof(long) * 2, "Start",
                    (int)sizeof(long) * 2, "End",
                    (int)sizeof(int)  * 2, "Size ",
@@ -719,7 +719,7 @@ dump_vm_map_entry(kvm_t *kd, struct kbit
        name = findname(kd, vmspace, vme, vp, vfs, uvm_obj);
 
        if (print_map) {
-               printf("0x%-*lx 0x%-*lx %c%c%c%c%c %c%c%c %s %s %d %d %d",
+               printf("0x%-*lx 0x%-*lx %c%c%c%c%c%c %c%c%c %s %s %d %d %d",
                    (int)sizeof(long) * 2 + 0, vme->start,
                    (int)sizeof(long) * 2 + 0, vme->end,
                    (vme->protection & PROT_READ) ? 'r' : '-',
@@ -727,6 +727,7 @@ dump_vm_map_entry(kvm_t *kd, struct kbit
                    (vme->protection & PROT_EXEC) ? 'x' : '-',
                    (vme->etype & UVM_ET_STACK) ? 'S' : '-',
                    (vme->etype & UVM_ET_SYSCALL) ? 'e' : '-',
+                   (vme->etype & UVM_ET_IMMUTABLE) ? 'I' : '-',
                    (vme->max_protection & PROT_READ) ? 'r' : '-',
                    (vme->max_protection & PROT_WRITE) ? 'w' : '-',
                    (vme->max_protection & PROT_EXEC) ? 'x' : '-',
@@ -746,7 +747,7 @@ dump_vm_map_entry(kvm_t *kd, struct kbit
        }
 
        if (print_maps)
-               printf("0x%-*lx 0x%-*lx %c%c%c%c%c%c %0*lx %02x:%02x %llu     
%s\n",
+               printf("0x%-*lx 0x%-*lx %c%c%c%c%c%c%c %0*lx %02x:%02x %llu     
%s\n",
                    (int)sizeof(void *) * 2, vme->start,
                    (int)sizeof(void *) * 2, vme->end,
                    (vme->protection & PROT_READ) ? 'r' : '-',
@@ -754,6 +755,7 @@ dump_vm_map_entry(kvm_t *kd, struct kbit
                    (vme->protection & PROT_EXEC) ? 'x' : '-',
                    (vme->etype & UVM_ET_STACK) ? 'S' : '-',
                    (vme->etype & UVM_ET_SYSCALL) ? 'e' : '-',
+                   (vme->etype & UVM_ET_IMMUTABLE) ? 'I' : '-',
                    (vme->etype & UVM_ET_COPYONWRITE) ? 'p' : 's',
                    (int)sizeof(void *) * 2,
                    (unsigned long)vme->offset,
@@ -767,13 +769,14 @@ dump_vm_map_entry(kvm_t *kd, struct kbit
                    vme->object.uvm_obj, (unsigned long)vme->offset,
                    vme->aref.ar_amap, vme->aref.ar_pageoff);
                printf("\tsubmap=%c, cow=%c, nc=%c, stack=%c, "
-                   "syscall=%c, prot(max)=%d/%d, inh=%d, "
+                   "syscall=%c, immutable=%c, prot(max)=%d/%d, inh=%d, "
                    "wc=%d, adv=%d\n",
                    (vme->etype & UVM_ET_SUBMAP) ? 'T' : 'F',
                    (vme->etype & UVM_ET_COPYONWRITE) ? 'T' : 'F',
                    (vme->etype & UVM_ET_NEEDSCOPY) ? 'T' : 'F',
                    (vme->etype & UVM_ET_STACK) ? 'T' : 'F',
                    (vme->etype & UVM_ET_SYSCALL) ? 'T' : 'F',
+                   (vme->etype & UVM_ET_IMMUTABLE) ? 'T' : 'F',
                    vme->protection, vme->max_protection,
                    vme->inheritance, vme->wired_count, vme->advice);
                if (inode && verbose)
@@ -813,7 +816,7 @@ dump_vm_map_entry(kvm_t *kd, struct kbit
                }
 
                sz = (size_t)((vme->end - vme->start) / 1024);
-               printf("%0*lx-%0*lx %7luk %0*lx %c%c%c%c%c%c%c (%c%c%c) 
%d/%d/%d %02u:%02u %7llu - %s",
+               printf("%0*lx-%0*lx %7luk %0*lx %c%c%c%c%c%c%c%c (%c%c%c) 
%d/%d/%d %02u:%02u %7llu - %s",
                    (int)sizeof(void *) * 2, vme->start, (int)sizeof(void *) * 
2,
                    vme->end - (vme->start != vme->end ? 1 : 0), (unsigned 
long)sz,
                    (int)sizeof(void *) * 2, (unsigned long)vme->offset,
@@ -822,6 +825,7 @@ dump_vm_map_entry(kvm_t *kd, struct kbit
                    (vme->protection & PROT_EXEC) ? 'x' : '-',
                    (vme->etype & UVM_ET_STACK) ? 'S' : '-',
                    (vme->etype & UVM_ET_SYSCALL) ? 'e' : '-',
+                   (vme->etype & UVM_ET_IMMUTABLE) ? 'I' : '-',
                    (vme->etype & UVM_ET_COPYONWRITE) ? 'p' : 's',
                    (vme->etype & UVM_ET_NEEDSCOPY) ? '+' : '-',
                    (vme->max_protection & PROT_READ) ? 'r' : '-',

Reply via email to