Theo de Raadt <dera...@openbsd.org> wrote: > 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...
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 -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 18:55:55 -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 18:52:16 -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' : '-',