Subject: using an elf note for paxctl instead of os bits...
To: None <tech-kern@netbsd.org>
From: Christos Zoulas <christos@zoulas.com>
List: tech-kern
Date: 06/13/2007 12:48:44
Hi,

I've been asked by core to provide a diff to use an elf note instead of
the os bits in the elf flags, so that we don't waste the bits. Ultimately
this could use fileassoc, but it is a lot more work. Here's the diff I
am planning to apply. Comments?

christos

Index: sys/kern/exec_elf32.c
===================================================================
RCS file: /cvsroot/src/sys/kern/exec_elf32.c,v
retrieving revision 1.123
diff -u -u -r1.123 exec_elf32.c
--- sys/kern/exec_elf32.c	22 Apr 2007 08:30:00 -0000	1.123
+++ sys/kern/exec_elf32.c	13 Jun 2007 16:44:35 -0000
@@ -699,11 +699,7 @@
 		case PT_DYNAMIC:
 			break;
 		case PT_NOTE:
-#if defined(PAX_MPROTECT) || defined(PAX_SEGVGUARD)
-			pax_adjust(l, ph[i].p_flags);
-#endif /* PAX_MPROTECT || PAX_SEGVGUARD */
 			break;
-
 		case PT_PHDR:
 			/* Note address of program headers (in text segment) */
 			phdr = pp->p_vaddr;
@@ -717,6 +713,11 @@
 		}
 	}
 
+#if defined(PAX_MPROTECT) || defined(PAX_SEGVGUARD)
+	if (epp->ep_pax_flags)
+		pax_adjust(l, epp->ep_pax_flags);
+#endif /* PAX_MPROTECT || PAX_SEGVGUARD */
+
 	/*
 	 * Check if we found a dynamically linked binary and arrange to load
 	 * its interpreter
@@ -771,7 +772,10 @@
 	Elf_Phdr *ph;
 	size_t phsize;
 	int error;
+	int isnetbsd = 0;
+	char *ndata;
 
+	epp->ep_pax_flags = 0;
 	if (eh->e_phnum > MAXPHNUM)
 		return ENOEXEC;
 
@@ -796,23 +800,38 @@
 		if (error)
 			goto next;
 
-		if (np->n_type != ELF_NOTE_TYPE_NETBSD_TAG ||
-		    np->n_namesz != ELF_NOTE_NETBSD_NAMESZ ||
-		    np->n_descsz != ELF_NOTE_NETBSD_DESCSZ ||
-		    memcmp(np + 1, ELF_NOTE_NETBSD_NAME,
-		    ELF_NOTE_NETBSD_NAMESZ))
-			goto next;
+		ndata = (char *)(np + 1);
+		switch (np->n_type) {
+		case ELF_NOTE_TYPE_NETBSD_TAG:
+			if (np->n_namesz != ELF_NOTE_NETBSD_NAMESZ ||
+			    np->n_descsz != ELF_NOTE_NETBSD_DESCSZ ||
+			    memcmp(ndata, ELF_NOTE_NETBSD_NAME,
+			    ELF_NOTE_NETBSD_NAMESZ))
+				goto next;
+			isnetbsd = 1;
+			break;
 
-		error = 0;
-		free(np, M_TEMP);
-		goto out;
+		case ELF_NOTE_TYPE_PAX_TAG:
+			if (np->n_namesz != ELF_NOTE_PAX_NAMESZ ||
+			    np->n_descsz != ELF_NOTE_PAX_DESCSZ ||
+			    memcmp(ndata, ELF_NOTE_PAX_NAME,
+			    ELF_NOTE_PAX_NAMESZ))
+				goto next;
+			(void)memcpy(&epp->ep_pax_flags,
+			    ndata + ELF_NOTE_PAX_NAMESZ,
+			    sizeof(epp->ep_pax_flags));
+			break;
+
+		default:
+			break;
+		}
 
-	next:
+next:
 		free(np, M_TEMP);
 		continue;
 	}
 
-	error = ENOEXEC;
+	error = isnetbsd ? 0 : ENOEXEC;
 out:
 	free(ph, M_TEMP);
 	return error;
Index: sys/kern/kern_pax.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_pax.c,v
retrieving revision 1.15
diff -u -u -r1.15 kern_pax.c
--- sys/kern/kern_pax.c	22 Feb 2007 06:34:43 -0000	1.15
+++ sys/kern/kern_pax.c	13 Jun 2007 16:44:35 -0000
@@ -200,14 +200,14 @@
 }
 
 void
-pax_adjust(struct lwp *l, int f)
+pax_adjust(struct lwp *l, uint32_t f)
 {
 #ifdef PAX_MPROTECT
 	if (pax_mprotect_enabled) {
-		if (f & PF_PAXMPROTECT)
+		if (f & ELF_NOTE_PAX_MPROTECT)
 			proc_setspecific(l->l_proc, pax_mprotect_key,
 			    PAX_MPROTECT_EXPLICIT_ENABLE);
-		if (f & PF_PAXNOMPROTECT)
+		if (f & ELF_NOTE_PAX_NOMPROTECT)
 			proc_setspecific(l->l_proc, pax_mprotect_key,
 			    PAX_MPROTECT_EXPLICIT_DISABLE);
 	}
@@ -215,11 +215,10 @@
 
 #ifdef PAX_SEGVGUARD
 	if (pax_segvguard_enabled) {
-		if (f & PF_PAXGUARD) {
+		if (f & ELF_NOTE_PAX_GUARD)
 			proc_setspecific(l->l_proc, pax_segvguard_key,
 			    PAX_SEGVGUARD_EXPLICIT_ENABLE);
-		}
-		if (f & PF_PAXNOGUARD)
+		if (f & ELF_NOTE_PAX_NOGUARD)
 			proc_setspecific(l->l_proc, pax_segvguard_key,
 			    PAX_SEGVGUARD_EXPLICIT_DISABLE);
 	}
Index: sys/sys/exec.h
===================================================================
RCS file: /cvsroot/src/sys/sys/exec.h,v
retrieving revision 1.116
diff -u -u -r1.116 exec.h
--- sys/sys/exec.h	22 Apr 2007 08:30:02 -0000	1.116
+++ sys/sys/exec.h	13 Jun 2007 16:44:39 -0000
@@ -201,6 +201,7 @@
 	const struct	execsw *ep_esch;/* execsw entry */
 	struct vnode *ep_emul_root;     /* base of emulation filesystem */
 	struct vnode *ep_interp;        /* vnode of (elf) interpeter */
+	uint32_t ep_pax_flags;		/* pax flags */
 };
 #define	EXEC_INDIR	0x0001		/* script handling already done */
 #define	EXEC_HASFD	0x0002		/* holding a shell script */
Index: sys/sys/exec_elf.h
===================================================================
RCS file: /cvsroot/src/sys/sys/exec_elf.h,v
retrieving revision 1.89
diff -u -u -r1.89 exec_elf.h
--- sys/sys/exec_elf.h	22 Nov 2006 15:08:47 -0000	1.89
+++ sys/sys/exec_elf.h	13 Jun 2007 16:44:40 -0000
@@ -346,11 +346,6 @@
 #define	PF_W		0x2	/* Segment is writable */
 #define	PF_X		0x1	/* Segment is executable */
 
-#define	PF_PAXMPROTECT		0x08000000	/* Explicitly enable PaX MPROTECT */
-#define	PF_PAXNOMPROTECT	0x04000000	/* Explicitly disable PaX MPROTECT */
-#define	PF_PAXGUARD		0x02000000	/* Explicitly enable PaX Segvguard */
-#define	PF_PAXNOGUARD		0x01000000	/* Explicitly disable PaX Segvguard */
-
 #define	PF_MASKOS	0x0ff00000	/* Operating system specific values */
 #define	PF_MASKPROC	0xf0000000	/* Processor-specific values */
 
@@ -678,6 +673,12 @@
 
 /* NetBSD-specific note type: Emulation name.  desc is emul name string. */
 #define	ELF_NOTE_TYPE_NETBSD_TAG	1
+/* NetBSD-specific note name and description sizes */
+#define	ELF_NOTE_NETBSD_NAMESZ		7
+#define	ELF_NOTE_NETBSD_DESCSZ		4
+/* NetBSD-specific note name */
+#define	ELF_NOTE_NETBSD_NAME		"NetBSD\0\0"
+
 /* NetBSD-specific note type: Checksum.  There should be 1 NOTE per PT_LOAD
    section.  desc is a tuple of <phnum>(16),<chk-type>(16),<chk-value>. */
 #define	ELF_NOTE_TYPE_CHECKSUM_TAG	2
@@ -686,11 +687,16 @@
 #define	ELF_NOTE_CHECKSUM_SHA1		3
 #define	ELF_NOTE_CHECKSUM_SHA256	4
 
-/* NetBSD-specific note name and description sizes */
-#define	ELF_NOTE_NETBSD_NAMESZ		7
-#define	ELF_NOTE_NETBSD_DESCSZ		4
-/* NetBSD-specific note name */
-#define	ELF_NOTE_NETBSD_NAME		"NetBSD\0\0"
+/* NetBSD-specific note type: PaX.  There should be 1 NOTE per executable.
+   section.  desc is a 32 bit bitmask */
+#define ELF_NOTE_TYPE_PAX_TAG		3
+#define	ELF_NOTE_PAX_MPROTECT		0x1	/* Force enable Mprotect */
+#define	ELF_NOTE_PAX_NOMPROTECT		0x2	/* Force disable Mprotect */
+#define	ELF_NOTE_PAX_GUARD		0x4	/* Force enable Segvguard */
+#define	ELF_NOTE_PAX_NOGUARD		0x8	/* Force disable Servguard */
+#define ELF_NOTE_PAX_NAMESZ		4
+#define ELF_NOTE_PAX_NAME		"PaX\0"
+#define ELF_NOTE_PAX_DESCSZ		4
 
 /*
  * NetBSD-specific core file information.
Index: sys/sys/pax.h
===================================================================
RCS file: /cvsroot/src/sys/sys/pax.h,v
retrieving revision 1.8
diff -u -u -r1.8 pax.h
--- sys/sys/pax.h	21 Feb 2007 23:00:10 -0000	1.8
+++ sys/sys/pax.h	13 Jun 2007 16:44:40 -0000
@@ -35,7 +35,7 @@
 struct lwp;
 
 void pax_init(void);
-void pax_adjust(struct lwp *, int);
+void pax_adjust(struct lwp *, uint32_t);
 
 void pax_mprotect(struct lwp *, vm_prot_t *, vm_prot_t *);
 
Index: usr.sbin/paxctl/paxctl.c
===================================================================
RCS file: /cvsroot/src/usr.sbin/paxctl/paxctl.c,v
retrieving revision 1.1
diff -u -u -r1.1 paxctl.c
--- usr.sbin/paxctl/paxctl.c	30 Jan 2007 19:40:08 -0000	1.1
+++ usr.sbin/paxctl/paxctl.c	13 Jun 2007 16:44:49 -0000
@@ -56,13 +56,17 @@
 static int pax_haveflags(u_long);
 static void pax_printflags(u_long);
 
-#ifndef PF_PAXMPROTECT
-#define PF_PAXMPROTECT		0x08000000
-#define PF_PAXNOMPROTECT	0x04000000
-#endif
-#ifndef PF_PAXGUARD
-#define PF_PAXGUARD		0x02000000
-#define PF_PAXNOGUARD		0x01000000
+#ifndef ELF_NOTE_TYPE_PAX_TAG
+/* NetBSD-specific note type: PaX.  There should be 1 NOTE per executable.
+   section.  desc is a 32 bit bitmask */
+#define ELF_NOTE_TYPE_PAX_TAG		3
+#define	ELF_NOTE_PAX_MPROTECT		0x1	/* Force enable Mprotect */
+#define	ELF_NOTE_PAX_NOMPROTECT		0x2	/* Force disable Mprotect */
+#define	ELF_NOTE_PAX_GUARD		0x4	/* Force enable Segvguard */
+#define	ELF_NOTE_PAX_NOGUARD		0x8	/* Force disable Servguard */
+#define ELF_NOTE_PAX_NAMESZ		4
+#define ELF_NOTE_PAX_NAME		"PaX\0"
+#define ELF_NOTE_PAX_DESCSZ		4
 #endif
 #ifndef __arraycount
 #define __arraycount(a) (sizeof(a) / sizeof(a[0]))
@@ -74,10 +78,14 @@
 	const char *name;
 	int bits;
 } flags[] = {
-	{ 'G', "Segvguard, explicit enable", PF_PAXGUARD },
-	{ 'g', "Segvguard, explicit disable", PF_PAXNOGUARD },
-	{ 'M', "mprotect(2) restrictions, explicit enable", PF_PAXMPROTECT },
-	{ 'm', "mprotect(2) restrictions, explicit disable", PF_PAXNOMPROTECT },
+	{ 'G', "Segvguard, explicit enable",
+	  ELF_NOTE_PAX_GUARD },
+	{ 'g', "Segvguard, explicit disable",
+	  ELF_NOTE_PAX_NOGUARD },
+	{ 'M', "mprotect(2) restrictions, explicit enable",
+	  ELF_NOTE_PAX_MPROTECT },
+	{ 'm', "mprotect(2) restrictions, explicit disable",
+	  ELF_NOTE_PAX_NOMPROTECT },
 };
 
 static void
@@ -156,8 +164,13 @@
 	    Elf32_Phdr h32;
 	    Elf64_Phdr h64;
 	} p;
+	union {
+	    Elf32_Nhdr h32;
+	    Elf64_Nhdr h64;
+	} n;
 #define EH(field)	(size == 32 ? e.h32.field : e.h64.field)
 #define PH(field)	(size == 32 ? p.h32.field : p.h64.field)
+#define NH(field)	(size == 32 ? n.h32.field : n.h64.field)
 #define SPH(field, val)	do { \
     if (size == 32) \
 	    p.h32.field val; \
@@ -165,6 +178,11 @@
 	    p.h64.field val; \
 } while (/*CONSTCOND*/0)
 #define PHSIZE		(size == 32 ? sizeof(p.h32) : sizeof(p.h64))
+#define NHSIZE		(size == 32 ? sizeof(n.h32) : sizeof(n.h64))
+	struct {
+		char name[ELF_NOTE_PAX_NAMESZ];
+		uint32_t flags;
+	} pax_tag;
 
 	int size;
 	char *opt = NULL;
@@ -227,41 +245,57 @@
 	for (i = 0; i < EH(e_phnum); i++) {
 		if (pread(fd, &p, PHSIZE,
 		    (off_t)EH(e_phoff) + i * PHSIZE) != PHSIZE)
-			err(EXIT_FAILURE, "Can't read data from `%s'", opt);
+			err(EXIT_FAILURE, "Can't read program header data"
+			    " from `%s'", opt);
 
 		if (PH(p_type) != PT_NOTE)
 			continue;
 
+		if (pread(fd, &n, NHSIZE, (off_t)PH(p_offset)) != NHSIZE)
+			err(EXIT_FAILURE, "Can't read note header from `%s'",
+			    opt);
+		if (NH(n_type) != ELF_NOTE_TYPE_PAX_TAG ||
+		    NH(n_descsz) != ELF_NOTE_PAX_DESCSZ ||
+		    NH(n_namesz) != ELF_NOTE_PAX_NAMESZ)
+			continue;
+		if (pread(fd, &pax_tag, sizeof(pax_tag), PH(p_offset) + NHSIZE)
+		    != sizeof(pax_tag))
+			err(EXIT_FAILURE, "Can't read pax_tag from `%s'",
+			    opt);
+		if (memcmp(pax_tag.name, ELF_NOTE_PAX_NAME,
+		    sizeof(pax_tag.name)) != 0)
+			err(EXIT_FAILURE, "Unknown pax_tag name `%*.*s' from"
+			    " `%s'", ELF_NOTE_PAX_NAMESZ, ELF_NOTE_PAX_NAMESZ,
+			    pax_tag.name, opt);
 		ok = 1;
 
 		if (list) {
-			if (!pax_haveflags((u_long)PH(p_flags)))
+			if (!pax_haveflags(pax_tag.flags))
 				break;
 
-			if (!pax_flags_sane((u_long)PH(p_flags)))
-				warnx("Current flags %lx don't make sense",
-				    (u_long)PH(p_flags));
+			if (!pax_flags_sane(pax_tag.flags))
+				warnx("Current flags %x don't make sense",
+				    pax_tag.flags);
 
 			(void)printf("PaX flags:\n");
 
-			pax_printflags((u_long)PH(p_flags));
+			pax_printflags(pax_tag.flags);
 
 			flagged = 1;
 
 			break;
 		}
 
-		SPH(p_flags, |= add_flags);
-		SPH(p_flags, &= ~del_flags);
+		pax_tag.flags |= add_flags;
+		pax_tag.flags &= ~del_flags;
 
-		if (!pax_flags_sane((u_long)PH(p_flags)))
-			errx(EXIT_FAILURE, "New flags %lx don't make sense",
-			    (u_long)PH(p_flags));
+		if (!pax_flags_sane(pax_tag.flags))
+			errx(EXIT_FAILURE, "New flags %x don't make sense",
+			    pax_tag.flags);
 
-		if (pwrite(fd, &p, PHSIZE,
-		    (off_t)EH(e_phoff) + i * PHSIZE) != PHSIZE)
+		if (pwrite(fd, &pax_tag, sizeof(pax_tag),
+		    (off_t)PH(p_offset) + NHSIZE) != sizeof(pax_tag))
 			err(EXIT_FAILURE, "Can't modify flags on `%s'", opt);
-
 		break;
 	}
 
@@ -269,7 +303,7 @@
 
 	if (!ok)
 		errx(EXIT_FAILURE,
-		    "Could not find an ELF PT_NOTE section in `%s'", opt);
+		    "Could not find an ELF PaX PT_NOTE section in `%s'", opt);
 
 	if (list && !flagged)
 		(void)printf("No PaX flags.\n");
Index: libexec/ld.elf_so/sysident.h
===================================================================
RCS file: /cvsroot/src/libexec/ld.elf_so/sysident.h,v
retrieving revision 1.13
diff -u -u -r1.13 sysident.h
--- libexec/ld.elf_so/sysident.h	13 Jun 2006 13:55:58 -0000	1.13
+++ libexec/ld.elf_so/sysident.h	13 Jun 2007 16:45:01 -0000
@@ -75,3 +75,17 @@
 	"\t.previous\n"
 	"\t.p2align\t2\n"
 );
+
+__asm(
+	".section\t\".note.netbsd.pax\", \"a\"\n"
+	"\t.p2align\t2\n\n"
+
+	"\t.long\t" __S(ELF_NOTE_PAX_NAMESZ) "\n"
+	"\t.long\t" __S(ELF_NOTE_PAX_DESCSZ) "\n"
+	"\t.long\t" __S(ELF_NOTE_TYPE_PAX_TAG) "\n"
+	"\t.ascii\t" __S(ELF_NOTE_PAX_NAME) "\n"
+	"\t.long\t" __S(0) "\n\n"
+
+	"\t.previous\n"
+	"\t.p2align\t2\n"
+);