Subject: Re: segvguard [was: Re: CVS commit: src/sys/sys]
To: Jason Thorpe <thorpej@shagadelic.org>
From: Elad Efrat <elad@NetBSD.org>
List: tech-security
Date: 12/03/2006 01:15:53
This is a multi-part message in MIME format.

--Boundary_(ID_D5bFvlqZhiJiqikoG37KJA)
Content-type: text/plain; charset=ISO-8859-1
Content-transfer-encoding: 7BIT

Jason Thorpe wrote:

> No, let's not invent a new ELF phdr type -- we don't control the ELF
> specification, but we do control the contents of our own PT_NOTE fields.

from christos, see attached patch. it moves the PaX flags to the netbsd
PT_NOTE. comments?

-e.

-- 
Elad Efrat

--Boundary_(ID_D5bFvlqZhiJiqikoG37KJA)
Content-type: text/plain; name=pax-note.diff
Content-transfer-encoding: 7BIT
Content-disposition: inline; filename=pax-note.diff

Index: libexec/ld.elf_so/sysident.h
===================================================================
RCS file: /usr/cvs/src/libexec/ld.elf_so/sysident.h,v
retrieving revision 1.13
diff -u -p -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	1 Dec 2006 21:49:16 -0000
@@ -75,3 +75,17 @@ __asm(
 	"\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"
+);
Index: sys/kern/exec_elf32.c
===================================================================
RCS file: /usr/cvs/src/sys/kern/exec_elf32.c,v
retrieving revision 1.120
diff -u -p -r1.120 exec_elf32.c
--- sys/kern/exec_elf32.c	24 Nov 2006 01:13:11 -0000	1.120
+++ sys/kern/exec_elf32.c	1 Dec 2006 21:48:42 -0000
@@ -692,11 +692,7 @@ exec_elf_makecmds(struct lwp *l, struct 
 		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;
@@ -710,6 +706,10 @@ exec_elf_makecmds(struct lwp *l, struct 
 		}
 	}
 
+#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
@@ -764,7 +764,10 @@ netbsd_elf_signature(struct lwp *l, stru
 	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;
 
@@ -789,23 +792,37 @@ netbsd_elf_signature(struct lwp *l, stru
 		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((caddr_t)(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;
 
-	next:
+		default:
+			break;
+		}
+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: /usr/cvs/src/sys/kern/kern_pax.c,v
retrieving revision 1.8
diff -u -p -r1.8 kern_pax.c
--- sys/kern/kern_pax.c	22 Nov 2006 02:02:51 -0000	1.8
+++ sys/kern/kern_pax.c	1 Dec 2006 21:48:42 -0000
@@ -193,14 +193,14 @@ pax_init(void)
 }
 
 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);
 	}
@@ -208,11 +208,11 @@ pax_adjust(struct lwp *l, int f)
 
 #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: /usr/cvs/src/sys/sys/exec.h,v
retrieving revision 1.114
diff -u -p -r1.114 exec.h
--- sys/sys/exec.h	30 Aug 2006 11:35:21 -0000	1.114
+++ sys/sys/exec.h	1 Dec 2006 21:48:42 -0000
@@ -200,6 +200,7 @@ struct exec_package {
 	void	*ep_emul_arg;		/* emulation argument */
 	const struct	execsw *ep_es;	/* appropriate execsw entry */
 	const struct	execsw *ep_esch;/* checked execsw entry */
+	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: /usr/cvs/src/sys/sys/exec_elf.h,v
retrieving revision 1.89
diff -u -p -r1.89 exec_elf.h
--- sys/sys/exec_elf.h	22 Nov 2006 15:08:47 -0000	1.89
+++ sys/sys/exec_elf.h	1 Dec 2006 21:48:42 -0000
@@ -346,11 +346,6 @@ typedef struct {
 #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 */
 
@@ -686,6 +681,17 @@ typedef struct {
 #define	ELF_NOTE_CHECKSUM_SHA1		3
 #define	ELF_NOTE_CHECKSUM_SHA256	4
 
+/* 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 note name and description sizes */
 #define	ELF_NOTE_NETBSD_NAMESZ		7
 #define	ELF_NOTE_NETBSD_DESCSZ		4
Index: sys/sys/pax.h
===================================================================
RCS file: /usr/cvs/src/sys/sys/pax.h,v
retrieving revision 1.5
diff -u -p -r1.5 pax.h
--- sys/sys/pax.h	22 Nov 2006 02:02:51 -0000	1.5
+++ sys/sys/pax.h	1 Dec 2006 21:48:42 -0000
@@ -38,7 +38,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.bin/paxctl/paxctl.c
===================================================================
RCS file: /usr/cvs/src/usr.bin/paxctl/paxctl.c,v
retrieving revision 1.10
diff -u -p -r1.10 paxctl.c
--- usr.bin/paxctl/paxctl.c	22 Nov 2006 15:08:47 -0000	1.10
+++ usr.bin/paxctl/paxctl.c	1 Dec 2006 21:49:16 -0000
@@ -59,13 +59,17 @@ static int pax_flags_sane(u_long);
 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]))
@@ -77,10 +81,14 @@ static const struct paxflag {
 	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
@@ -159,8 +167,13 @@ main(int argc, char **argv)
 	    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; \
@@ -168,6 +181,11 @@ main(int argc, char **argv)
 	    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;
@@ -230,41 +248,57 @@ main(int argc, char **argv)
 	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;
 	}
 
@@ -272,7 +306,7 @@ main(int argc, char **argv)
 
 	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");

--Boundary_(ID_D5bFvlqZhiJiqikoG37KJA)--