From 3f073d2edfec8b7c7e64932ad7e527f3c460a476 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh@NetBSD.org>
Date: Sun, 28 Apr 2024 04:20:53 +0000
Subject: [PATCH] WIP: try to put a guard page before _C_ctype_tab_ and similar

doesn't work, ls gets SIGSEGV with SEGV_MAPERR
---
 lib/libc/Makefile     |   3 +
 lib/libc/gen/ctype_.c |  22 ++++
 lib/libc/ldscript     | 281 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 306 insertions(+)
 create mode 100644 lib/libc/ldscript

diff --git a/lib/libc/Makefile b/lib/libc/Makefile
index fa9fd393d6fd..234c9522aa1d 100644
--- a/lib/libc/Makefile
+++ b/lib/libc/Makefile
@@ -157,6 +157,9 @@ FILESDIR=	/var/db
 # dlopen() from within statically linked binaries.
 CSHLIBFLAGS+=	-D_I18N_DYNAMIC
 
+LDFLAGS+=	-Wl,-T,${.CURDIR}/ldscript
+DPADD+=		${.CURDIR}/ldscript
+
 .include <bsd.lib.mk>
 
 # force the dynamic linker to initialize libc first
diff --git a/lib/libc/gen/ctype_.c b/lib/libc/gen/ctype_.c
index b12c41a17521..f0691d1dfc47 100644
--- a/lib/libc/gen/ctype_.c
+++ b/lib/libc/gen/ctype_.c
@@ -61,6 +61,17 @@ __RCSID("$NetBSD: ctype_.c,v 1.20 2013/04/13 10:21:20 joerg Exp $");
 #define	_B	_COMPAT_B
 #define	_N	_COMPAT_N
 
+#if 1
+asm("	.pushsection	\".guard.compat_ctype\",\"a\",@nobits\n\
+	.p2align	12\n\
+	.type		_C_compat_ctype_guard_,@object\n\
+	.size		_C_compat_ctype_guard_,4096\n\
+ _C_compat_ctype_guard:\n\
+	.zero		4096\n\
+	.popsection");
+asm(".pushsection \".grodata.compat_ctype\",\"a\",@progbits; .p2align 12; .popsection");
+__attribute__((__section__(".grodata.compat_ctype")))
+#endif
 const unsigned char _C_compat_bsdctype[1 + _CTYPE_NUM_CHARS] = {
 	0,
 	_C,	_C,	_C,	_C,	_C,	_C,	_C,	_C,
@@ -109,6 +120,17 @@ const unsigned char *_ctype_ = &_C_compat_bsdctype[0];
 #define	_U	_CTYPE_U
 #define	_X	_CTYPE_X
 
+#if 1
+asm("	.pushsection	\".guard.ctype\",\"a\",@nobits\n\
+	.p2align	12\n\
+	.type		_C_ctype_guard_,@object\n\
+	.size		_C_ctype_guard_,4096\n\
+ _C_ctype_guard:\n\
+	.zero		4096\n\
+	.popsection");
+asm(".pushsection \".grodata.ctype\",\"a\",@progbits; .p2align 12; .popsection");
+__attribute__((__section__(".grodata.ctype")))
+#endif
 const unsigned short _C_ctype_tab_[1 + _CTYPE_NUM_CHARS] = {
 	0,
 	_C,		_C,		_C,		_C,
diff --git a/lib/libc/ldscript b/lib/libc/ldscript
new file mode 100644
index 000000000000..725588425b4e
--- /dev/null
+++ b/lib/libc/ldscript
@@ -0,0 +1,281 @@
+/*	$NetBSD$	*/
+
+/* XXX adapted for guarded ctype/tolower/toupper */
+
+/* Default linker script, for normal executables */
+/* Copyright (C) 2014-2020 Free Software Foundation, Inc.
+   Copying and distribution of this script, with or without modification,
+   are permitted in any medium without royalty provided the copyright
+   notice and this notice are preserved.  */
+OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
+	      "elf64-x86-64")
+OUTPUT_ARCH(i386:x86-64)
+ENTRY(_start)
+SEARCH_DIR("=/usr/lib");
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
+  .interp         : { *(.interp) }
+  .note.gnu.build-id  : { *(.note.gnu.build-id) }
+  .hash           : { *(.hash) } :text
+  .gnu.hash       : { *(.gnu.hash) }
+  .dynsym         : { *(.dynsym) } :text
+  .dynstr         : { *(.dynstr) } :text
+  .gnu.version    : { *(.gnu.version) }
+  .gnu.version_d  : { *(.gnu.version_d) }
+  .gnu.version_r  : { *(.gnu.version_r) }
+/*
+  .rela.init      : { *(.rela.init) }
+  .rela.text      : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
+  .rela.fini      : { *(.rela.fini) }
+  .rela.rodata    : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
+  .rela.data.rel.ro   : { *(.rela.data.rel.ro .rela.data.rel.ro.* .rela.gnu.linkonce.d.rel.ro.*) }
+  .rela.data      : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
+  .rela.tdata	  : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
+  .rela.tbss	  : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
+  .rela.ctors     : { *(.rela.ctors) }
+  .rela.dtors     : { *(.rela.dtors) }
+  .rela.got       : { *(.rela.got) }
+  .rela.bss       : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
+  .rela.ldata     : { *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*) }
+  .rela.lbss      : { *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*) }
+  .rela.lrodata   : { *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*) }
+  .rela.ifunc     : { *(.rela.ifunc) }
+*/
+  .rela.dyn       : { *(.rela.dyn) } :text
+  .rela.plt       :
+    {
+      *(.rela.plt)
+      PROVIDE_HIDDEN (__rela_iplt_start = .);
+      *(.rela.iplt)
+      PROVIDE_HIDDEN (__rela_iplt_end = .);
+    } :text
+  .init           :
+  {
+    KEEP (*(SORT_NONE(.init)))
+  } :text
+  .plt            : { *(.plt) *(.iplt) } :text
+.plt.got        : { *(.plt.got) } :text
+.plt.sec        : { *(.plt.sec) }
+  .text           :
+  {
+    PROVIDE_HIDDEN (__eprol = .);
+    *(.text)
+    *(.text.unlikely .text.*_unlikely .text.unlikely.*)
+    *(.text.exit .text.exit.*)
+    *(.text.startup .text.startup.*)
+    *(.text.hot .text.hot.*)
+    *(SORT(.text.sorted.*))
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+    /* .gnu.warning sections are handled specially by elf.em.  */
+    *(.gnu.warning)
+  } :text
+  .fini           :
+  {
+    KEEP (*(SORT_NONE(.fini)))
+  } :text
+  PROVIDE (__etext = .);
+  PROVIDE (_etext = .);
+  PROVIDE (etext = .);
+  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) } :text
+  .rodata1        : { *(.rodata1) }
+  .eh_frame_hdr   : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } :text :gnu_eh_frame
+  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } :text
+  .note.netbsd.ident : { *(.note.netbsd.ident) } :text :note
+  .note.netbsd.pax : { *(.note.netbsd.pax) } :text :note
+  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
+  .gnu_extab   : ONLY_IF_RO { *(.gnu_extab*) }
+  /* These sections are generated by the Sun/Oracle C++ compiler.  */
+  .exception_ranges   : ONLY_IF_RO { *(.exception_ranges*) }
+  /* Adjust the address for the data segment.  We want to adjust up to
+     the same address within the page on the next page up.  */
+  . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
+  /* Exception handling  */
+  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
+  .gnu_extab      : ONLY_IF_RW { *(.gnu_extab) }
+  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
+  .exception_ranges   : ONLY_IF_RW { *(.exception_ranges*) }
+  /* Thread Local Storage sections  */
+  .tdata	  :
+   {
+     PROVIDE_HIDDEN (__tdata_start = .);
+     *(.tdata .tdata.* .gnu.linkonce.td.*)
+   } :data :tls
+  .tbss		  : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } :tls
+  .preinit_array    :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  }
+  .init_array    :
+  {
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
+    KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
+    PROVIDE_HIDDEN (__init_array_end = .);
+  } :data :gnu_relro
+  .fini_array    :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
+    KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  } :data
+  .ctors          :
+  {
+    /* gcc uses crtbegin.o to find the start of
+       the constructors, so we make sure it is
+       first.  Because this is a wildcard, it
+       doesn't matter if the user does not
+       actually link against crtbegin.o; the
+       linker won't look for a file to match a
+       wildcard.  The wildcard also means that it
+       doesn't matter which directory crtbegin.o
+       is in.  */
+    KEEP (*crtbegin.o(.ctors))
+    KEEP (*crtbegin?.o(.ctors))
+    /* We don't want to include the .ctor section from
+       the crtend.o file until after the sorted ctors.
+       The .ctor section from the crtend file contains the
+       end of ctors marker and it must be last */
+    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+  } :data
+  .dtors          :
+  {
+    KEEP (*crtbegin.o(.dtors))
+    KEEP (*crtbegin?.o(.dtors))
+    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+  } :data
+  .jcr            : { KEEP (*(.jcr)) } :data
+  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } :data
+  .dynamic        : { *(.dynamic) } :data :dynamic
+  .got            : { *(.got) *(.igot) } :data
+  . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
+  .got.plt        : { *(.got.plt) *(.igot.plt) } :data
+  .data           :
+  {
+    *(.data .data.* .gnu.linkonce.d.*)
+    SORT(CONSTRUCTORS)
+  } :data
+  .data1          : { *(.data1) }
+  _edata = .; PROVIDE (edata = .);
+  . = .;
+  __bss_start = .;
+  .bss            :
+  {
+   *(.dynbss)
+   *(.bss .bss.* .gnu.linkonce.b.*)
+   *(COMMON)
+   /* Align here to ensure that the .bss section occupies space up to
+      _end.  Align after .bss to ensure correct alignment even if the
+      .bss section disappears because there are no input sections.
+      FIXME: Why do we need it? When there is no .bss section, we do not
+      pad the .data section.  */
+   . = ALIGN(. != 0 ? 64 / 8 : 1);
+  } :data
+  .lbss   :
+  {
+    *(.dynlbss)
+    *(.lbss .lbss.* .gnu.linkonce.lb.*)
+    *(LARGE_COMMON)
+  }
+  . = ALIGN(64 / 8);
+  . = SEGMENT_START("ldata-segment", .);
+  .lrodata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
+  {
+    *(.lrodata .lrodata.* .gnu.linkonce.lr.*)
+  }
+  .ldata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
+  {
+    *(.ldata .ldata.* .gnu.linkonce.l.*)
+    . = ALIGN(. != 0 ? 64 / 8 : 1);
+  }
+
+
+  /* XXX BEGIN hack guarded ctype, tolower, toupper tables */
+  . = ALIGN (4096);
+  .guard.compat_ctype : { *(.guard.compat_ctype) } :guard_compat_ctype
+  . = ALIGN (4096);
+  .grodata.compat_ctype : { *(.grodata.compat_ctype) } :grodata_compat_ctype
+  . = ALIGN (4096);
+  .guard.ctype    : { *(.guard.ctype) } :guard_ctype
+  . = ALIGN (4096);
+  .grodata.ctype  : { *(.grodata.ctype) } :grodata_ctype
+  . = ALIGN (4096);
+  .guard.tolower  : { *(.guard.tolower) } :guard_tolower
+  . = ALIGN (4096);
+  .grodata.tolower : { *(.grodata.tolower) } :grodata_tolower
+  . = ALIGN (4096);
+  .guard.toupper  : { *(.guard.toupper) } :guard_toupper
+  . = ALIGN (4096);
+  .grodata.toupper : { *(.grodata.toupper) } :grodata_toupper
+  /* XXX END hack guarded ctype, tolower, toupper tables */
+
+  . = ALIGN(64 / 8);
+  _end = .; PROVIDE (end = .);
+  . = DATA_SEGMENT_END (.);
+  /* Stabs debugging sections.  */
+  .stab          0 : { *(.stab) }
+  .stabstr       0 : { *(.stabstr) }
+  .stab.excl     0 : { *(.stab.excl) }
+  .stab.exclstr  0 : { *(.stab.exclstr) }
+  .stab.index    0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment       0 : { *(.comment) }
+  .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }
+  /* DWARF debug sections.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line .debug_line.* .debug_line_end) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+  /* DWARF 3 */
+  .debug_pubtypes 0 : { *(.debug_pubtypes) }
+  .debug_ranges   0 : { *(.debug_ranges) }
+  /* DWARF Extension.  */
+  .debug_macro    0 : { *(.debug_macro) }
+  .debug_addr     0 : { *(.debug_addr) }
+  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
+  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
+}
+
+PHDRS
+{
+  headers PT_PHDR PHDRS;
+  text PT_LOAD FILEHDR PHDRS;
+  data PT_LOAD;
+  dynamic PT_DYNAMIC;
+  note PT_NOTE;
+  tls PT_TLS FLAGS (4);
+  gnu_eh_frame 0x6474e550; /* PT_GNU_EH_FRAME */
+  gnu_relro 0x6474e552; /* PT_GNU_RELRO */
+  guard_compat_ctype PT_LOAD FLAGS (0);
+  grodata_compat_ctype PT_LOAD;
+  guard_ctype PT_LOAD FLAGS (0);
+  grodata_ctype PT_LOAD;
+}