Re: immutable userland mappings

2022-09-28 Thread Theo de Raadt
Theo de Raadt  wrote:

> Theo de Raadt  wrote:
> 
> > > Yet another version of the diff as I incrementally get it working better.
> > > Call it version 22..
> 
> This is around version 30.

New version.

uvm_unmap_remove() now avoids doing entry splits as it scans for immutables
in the region.

Lots of // debug code remains, because there is a hunt for a linker bug;
some libraries are created subtly incorrectly, and it would be better if
they were correct and we could rip out some workarounds.

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 -  1.1.1.4
+++ gnu/llvm/lld/ELF/ScriptParser.cpp   17 Sep 2022 13:11:25 -
@@ -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 -  1.3
+++ gnu/llvm/lld/ELF/Writer.cpp 17 Sep 2022 13:11:25 -
@@ -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 Writer::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/llvm/llvm/include/llvm/BinaryFormat/ELF.h
===
RCS file: /cvs/src/gnu/llvm/llvm/include/llvm/BinaryFormat/ELF.h,v
retrieving revision 1.1.1.3
diff -u -p -u -r1.1.1.3 ELF.h
--- gnu/llvm/llvm/include/llvm/BinaryFormat/ELF.h   17 Dec 2021 12:23:22 
-  1.1.1.3
+++ gnu/llvm/llvm/include/llvm/BinaryFormat/ELF.h   17 Sep 2022 13:11:25 
-
@@ -1303,6 +1303,7 @@ enum {
   PT_GNU_RELRO = 0x6474e552,// Read-only after relocation.
   PT_GNU_PROPERTY = 0x6474e553, // .note.gnu.property notes sections.
 
+  PT_OPENBSD_MUTABLE = 0x65a3dbe5, // Like bss, but not immutable.
   PT_OPENBSD_RANDOMIZE = 0x65a3dbe6, // Fill with random data.
   PT_OPENBSD_WXNEEDED = 0x65a3dbe7,  // Program does W^X violations.
   PT_OPENBSD_BOOTDATA = 0x65a41be6,  // Section for boot arguments.
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 -  1.23
+++ gnu/usr.bin/binutils/bfd/elf.c  17 Sep 2022 13:11:25 -
@@ -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 off0x", 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 

Re: immutable userland mappings

2022-09-15 Thread Crystal Kolipe
On Thu, Sep 15, 2022 at 08:31:06AM -0600, Theo de Raadt wrote:
> RCS file: lib/libc/sys/mimmutable.2
...
> +Unmapped pages in the region do not retain immutability, but this
> +behaviour should not be relied up.

s/relied up/relied on/

or

s/relied up/relied upon/



Re: immutable userland mappings

2022-09-15 Thread Theo de Raadt
Theo de Raadt  wrote:

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

This is around version 30.

There is still a subtle problem with RELRO, but it is masked with a hack.
arm64 also works correctly, and I'm onto the next architecture.  Systems
using bfd.ld need to be checked also.  There's a weird piece of logic
related to "libraries that never unload", I am not sure I did it right.

man page changes included.

Transition steps are:

   make new kernel, install, reboot
   [you will see uvm_unmap errors for various programs in dmesg]
   make includes
   cd gnu/usr.bin/clang; make && make install
   make backup copies of /usr/libexec/ld.so and the most recent libc.so
   build and install ld.so
   [you will now see uvm_map_protect errors in dmesg]
   build and install libc.so
   [the messages should go away]
   build everything

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 -  1.1.1.4
+++ gnu/llvm/lld/ELF/ScriptParser.cpp   2 Sep 2022 15:23:20 -
@@ -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 -  1.3
+++ gnu/llvm/lld/ELF/Writer.cpp 2 Sep 2022 21:53:22 -
@@ -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 Writer::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/llvm/llvm/include/llvm/BinaryFormat/ELF.h
===
RCS file: /cvs/src/gnu/llvm/llvm/include/llvm/BinaryFormat/ELF.h,v
retrieving revision 1.1.1.3
diff -u -p -u -r1.1.1.3 ELF.h
--- gnu/llvm/llvm/include/llvm/BinaryFormat/ELF.h   17 Dec 2021 12:23:22 
-  1.1.1.3
+++ gnu/llvm/llvm/include/llvm/BinaryFormat/ELF.h   14 Sep 2022 13:01:42 
-
@@ -1303,6 +1303,7 @@ enum {
   PT_GNU_RELRO = 0x6474e552,// Read-only after relocation.
   PT_GNU_PROPERTY = 0x6474e553, // .note.gnu.property notes sections.
 
+  PT_OPENBSD_MUTABLE = 0x65a3dbe5, // Like bss, but not immutable.
   PT_OPENBSD_RANDOMIZE = 0x65a3dbe6, // Fill with random data.
   PT_OPENBSD_WXNEEDED = 0x65a3dbe7,  // Program does W^X violations.
   PT_OPENBSD_BOOTDATA = 0x65a41be6,  // Section for boot arguments.
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 -  1.23
+++ gnu/usr.bin/binutils/bfd/elf.c  14 Sep 2022 13:00:27 -
@@ -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 off0x", p

Re: immutable userland mappings

2022-09-10 Thread Theo de Raadt
Theo de Raadt  wrote:

> Theo de Raadt  wrote:
> 
> > Theo de Raadt  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...

Oops, 1 line error in the diff, try this instead.

And to point 1 above, notice that a rare page of shared library mapping
is not immutable, right on the edge of the relro...

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 -  1.1.1.4
+++ gnu/llvm/lld/ELF/ScriptParser.cpp   2 Sep 2022 15:23:20 -
@@ -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 -  1.3
+++ gnu/llvm/lld/ELF/Writer.cpp 2 Sep 2022 21:53:22 -
@@ -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 Writer::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.

Re: immutable userland mappings

2022-09-10 Thread Theo de Raadt
Theo de Raadt  wrote:

> Theo de Raadt  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 -  1.1.1.4
+++ gnu/llvm/lld/ELF/ScriptParser.cpp   2 Sep 2022 15:23:20 -
@@ -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 -  1.3
+++ gnu/llvm/lld/ELF/Writer.cpp 2 Sep 2022 21:53:22 -
@@ -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 Writer::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 -  1.23
+++ gnu/usr.bin/binutils/bfd/elf.c  10 Sep 2022 07:06:59 -
@@ -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 =

Re: immutable userland mappings

2022-09-03 Thread Theo de Raadt
Theo de Raadt  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.

I have mostly succeeded at changing this to a better model.  The
previous version had a real difficult time finding the specific objects
which cannot become immutable, so I found a way to describe them to the
kernel.

The ELF program headers now contain a new PHDR for section
".openbsd.mutable", aka OPENBSD_MUTABLE.  This is placed on the malloc
self-protected bookkeeping page.

When the main binary & ld.so are loaded the kernel can make all LOAD
regions immutable, and then in a later pass the kernel is permitted to
undo the immutable on the OPENBSD_MUTABLE and GNU_RELRO regions.  Only
the kernel can undo immutability, in these specific cases.

When crt0 finishes RELRO adjustment and mprotects PROT_READ, it also
marks the region immutable.  ld.so does the same for it's RELRO.

The libc malloc self-protection scheme can mprotect RW and R back and
forth when it wants, and it will never make this region immutable. 

When ld.so loads a library, it creates a queue of immutable and mutable
work actions to do after the library is done.  ld.so cannot reverse an
immutable, so it must clip the mutable regions out of the immutables.
This clip piece of the diff is embarrassingly poor.  It is very forgiving,
which is why I can run multiuser.  I do know that the system libraries are
clean, and anyways the big problems were with the malloc, relro, main
programs, and ld.so, not the other libraries

This version is harder to cross compile through, but I wanted to show in
case someone is interested.  There are numberous gaps (binutils lacks
support, other architectures, but the worst part is the immutuble
clipping code.


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 -r1.1.1.4 ScriptParser.cpp
--- gnu/llvm/lld/ELF/ScriptParser.cpp   17 Dec 2021 12:25:02 -  1.1.1.4
+++ gnu/llvm/lld/ELF/ScriptParser.cpp   2 Sep 2022 15:23:20 -
@@ -1478,6 +1478,7 @@
  .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 -r1.3 Writer.cpp
--- gnu/llvm/lld/ELF/Writer.cpp 17 Dec 2021 14:46:47 -  1.3
+++ gnu/llvm/lld/ELF/Writer.cpp 2 Sep 2022 21:53:22 -
@@ -146,7 +146,7 @@
{".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 @@
   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/llvm/llvm/include/llvm/BinaryFormat/ELF.h
===
RCS file: /cvs/src/gnu/llvm/llvm/include/llvm/BinaryFormat/ELF.h,v
retrieving revision 1.1.1.3
diff -u -r1.1.1.3 ELF.h
--- gnu/llvm/llvm/include/llvm/BinaryFormat/ELF.h   17 Dec 2021 12:23:22 
-  1.1.1.3
+++ gnu/llvm/llvm/include/llvm/BinaryFormat/ELF.h   2 Sep 2022 12:12:47 
-
@@ -1303,6 +1303,7 @@
   PT_GNU_RELRO = 0x6474e552,// Read-only after relocation.
   PT_GNU_PROPERTY = 0x6474e553, // .note.gnu.property notes sections.
 
+  PT_OPENBSD_MUTABLE = 0x65a3dbe5, // Like bss, but not immutable
   PT_OPENBSD_RANDOMIZE = 0x65a3dbe6, // Fill with random data.
   PT_OPENBSD

immutable userland mappings

2022-09-01 Thread Theo de Raadt
In the last few years, I have been improving the strictness of userland
memory layout.

An example is the recent addition of MAP_STACK and msyscall().  The first one
marks pages that are stack, so that upon entry to the kernel we can check if
the stack-pointer is pointing in the stack range.  If it isn't, the most obvious
conclusion is that a ROP pivot has occured, and we kills the process.  The 
second
one marks the region which contains syscall traps, if upon entry to the kernel
the PC is not in that region, we know somone is trying to do system calls via
an unapproved method.

My next attempt is to lock memory mappings.  The current working name is
mimmutable(void *addr, size_t len).  This identifies all current mapped memory
in a region, and tags the mappings.  Such mappings can never be unmapped.
No new mmap can be done on top of the mappings.  And the permissions cannot
be changed.  Other than that, the underlying storage memory works fine, it is
just the mapping that is locked.

I'm aware of an method used at least once (not on openbsd) which managed
to mprotect a region of libc, and then place things there, for later
execution.  That makes msyscall() less useful, so I want to restore the
strenth.

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.

So for now, crt0 self-immutables the remaining parts, and ld.so does the same
for itself.  ld.so is also responsible for immutable marking on libraries it
loads.

To my surprise, the diff is working quite well.  With kern.allowkmem=1,
"procmap -a" can see what process mappings look like, and they are quite
locked down.  This is a view of a "sed", with 'grep -v anon'.  The "I" marker
indicates mapping immutability.   A few small chunks must still be fixed.

StartEnd Size  Offset   rwxSeIpc  RWX  
I/W/A Dev Inode - File
03128621a000-03128621cfff  12k  rIp+ (rwx) 
1/0/0 04:05   77840 - /usr/bin/sed [0xfd83e38b3640]
03128621d000-031286222fff  24k 2000 r-x--Ip+ (rwx) 
1/0/0 04:05   77840 - /usr/bin/sed [0xfd83e38b3640]
03148bbc3000-03148bbc3fff   4k  rIs- (r--) 
1/0/1 00:00   0 -   [ uvm_aobj ]
0314ea4e7000-0314ea51dfff 220k  rIp+ (rwx) 
1/0/0 04:05  103688 - /usr/lib/libc.so.96.1 [0xfd83e0c62608]
0314ea51e000-0314ea5c2fff 660k 00036000 r-x-eIp+ (rwx) 
1/0/0 04:05  103688 - /usr/lib/libc.so.96.1 [0xfd83e0c62608]
0314ea5c3000-0314ea5c8fff  24k 000da000 rIp- (rwx) 
1/0/0 04:05  103688 - /usr/lib/libc.so.96.1 [0xfd83e0c62608]
0314ea5c9000-0314ea5cbfff  12k 000df000 rw---Ip- (rwx) 
1/0/0 04:05  103688 - /usr/lib/libc.so.96.1 [0xfd83e0c62608]
031526544000-031526544fff   4k  r-x-eIp+ (rwx) 
1/0/1 00:00   0 -   [ uvm_aobj ]
031535da7000-031535da9fff  12k  rIp+ (rwx) 
1/0/0 04:05  336963 - /usr/libexec/ld.so [0xfd83e27f90e8]
031535eac000-031535eb6fff  44k 5000 r-x-eIp+ (rwx) 
1/0/0 04:05  336963 - /usr/libexec/ld.so [0xfd83e27f90e8]
031535fa7000-031535fa7fff   4k 0001 r-p- (rwx) 
1/0/0 04:05  336963 - /usr/libexec/ld.so [0xfd83e27f90e8]
031535fa8000-031535fa8fff   4k 00011000 rwp- (rwx) 
1/0/0 04:05  336963 - /usr/libexec/ld.so [0xfd83e27f90e8]
03154a19e000-03154a1b1fff  80k  rIp+ (rwx) 
1/0/0 04:04 3654731 - /var/run/ld.so.hints [0xfd83e1098960]
7f7ffdebd000-7f7fffbbcfff   29696k  --p+ (rwx) 
1/0/0 00:00   0 -   [ stack ]
 total   5724k


I wanted to show it a bit early.  To try the diff:
  - apply patch
  - cd /usr/src/sys/kern; make syscalls
  - build and boot new kernel
  - cd /usr/src; make includes
  - cd lib/libc; make && make install
  - cd /usr/src/*/ld.so; make && make install
  - cd /usr/src/lib/csu; make && make install
  - then other binaries can be built, including procmap



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 -  1.33
+++ lib/csu/boot.h  31 Aug 2022 05:14:09 -
@@ -35,7 +35,6 @@
 #define_DYN_LOADER
 
 #include 
-#include 
 
 #include 
 
@@ -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
 typedefE