Use an elf note to handle pax arguments. This is a temporary solution to

avoid wasting OS flag bits. In the future we'll probably use fileassoc to
achieve this (once there is a way to make fileassoc persistent) or in the
shorter term libelf, so that we can add and remove the note on demand instead
of burning bits on each binary. Of course since this is a tool, this means
that we'll need to think about how to handle libelf...
This commit is contained in:
christos 2007-06-24 20:35:36 +00:00
parent e0e4b56127
commit 2ffe4b875f
7 changed files with 140 additions and 67 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: sysident.h,v 1.13 2006/06/13 13:55:58 simonb Exp $ */ /* $NetBSD: sysident.h,v 1.14 2007/06/24 20:35:36 christos Exp $ */
/* /*
* Copyright (c) 1997 Christopher G. Demetriou * Copyright (c) 1997 Christopher G. Demetriou
@ -75,3 +75,17 @@ __asm(
"\t.previous\n" "\t.previous\n"
"\t.p2align\t2\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"
);

View File

@ -1,4 +1,4 @@
/* $NetBSD: exec_elf32.c,v 1.123 2007/04/22 08:30:00 dsl Exp $ */ /* $NetBSD: exec_elf32.c,v 1.124 2007/06/24 20:35:37 christos Exp $ */
/*- /*-
* Copyright (c) 1994, 2000, 2005 The NetBSD Foundation, Inc. * Copyright (c) 1994, 2000, 2005 The NetBSD Foundation, Inc.
@ -64,7 +64,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(1, "$NetBSD: exec_elf32.c,v 1.123 2007/04/22 08:30:00 dsl Exp $"); __KERNEL_RCSID(1, "$NetBSD: exec_elf32.c,v 1.124 2007/06/24 20:35:37 christos Exp $");
/* If not included by exec_elf64.c, ELFSIZE won't be defined. */ /* If not included by exec_elf64.c, ELFSIZE won't be defined. */
#ifndef ELFSIZE #ifndef ELFSIZE
@ -699,11 +699,7 @@ exec_elf_makecmds(struct lwp *l, struct exec_package *epp)
case PT_DYNAMIC: case PT_DYNAMIC:
break; break;
case PT_NOTE: case PT_NOTE:
#if defined(PAX_MPROTECT) || defined(PAX_SEGVGUARD)
pax_adjust(l, ph[i].p_flags);
#endif /* PAX_MPROTECT || PAX_SEGVGUARD */
break; break;
case PT_PHDR: case PT_PHDR:
/* Note address of program headers (in text segment) */ /* Note address of program headers (in text segment) */
phdr = pp->p_vaddr; phdr = pp->p_vaddr;
@ -717,6 +713,11 @@ exec_elf_makecmds(struct lwp *l, struct exec_package *epp)
} }
} }
#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 * Check if we found a dynamically linked binary and arrange to load
* its interpreter * its interpreter
@ -771,7 +772,10 @@ netbsd_elf_signature(struct lwp *l, struct exec_package *epp,
Elf_Phdr *ph; Elf_Phdr *ph;
size_t phsize; size_t phsize;
int error; int error;
int isnetbsd = 0;
char *ndata;
epp->ep_pax_flags = 0;
if (eh->e_phnum > MAXPHNUM) if (eh->e_phnum > MAXPHNUM)
return ENOEXEC; return ENOEXEC;
@ -796,23 +800,38 @@ netbsd_elf_signature(struct lwp *l, struct exec_package *epp,
if (error) if (error)
goto next; goto next;
if (np->n_type != ELF_NOTE_TYPE_NETBSD_TAG || ndata = (char *)(np + 1);
np->n_namesz != ELF_NOTE_NETBSD_NAMESZ || switch (np->n_type) {
np->n_descsz != ELF_NOTE_NETBSD_DESCSZ || case ELF_NOTE_TYPE_NETBSD_TAG:
memcmp(np + 1, ELF_NOTE_NETBSD_NAME, if (np->n_namesz != ELF_NOTE_NETBSD_NAMESZ ||
ELF_NOTE_NETBSD_NAMESZ)) np->n_descsz != ELF_NOTE_NETBSD_DESCSZ ||
goto next; memcmp(ndata, ELF_NOTE_NETBSD_NAME,
ELF_NOTE_NETBSD_NAMESZ))
goto next;
isnetbsd = 1;
break;
error = 0; case ELF_NOTE_TYPE_PAX_TAG:
free(np, M_TEMP); if (np->n_namesz != ELF_NOTE_PAX_NAMESZ ||
goto out; 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); free(np, M_TEMP);
continue; continue;
} }
error = ENOEXEC; error = isnetbsd ? 0 : ENOEXEC;
out: out:
free(ph, M_TEMP); free(ph, M_TEMP);
return error; return error;

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_pax.c,v 1.15 2007/02/22 06:34:43 thorpej Exp $ */ /* $NetBSD: kern_pax.c,v 1.16 2007/06/24 20:35:37 christos Exp $ */
/*- /*-
* Copyright (c) 2006 Elad Efrat <elad@NetBSD.org> * Copyright (c) 2006 Elad Efrat <elad@NetBSD.org>
@ -200,14 +200,14 @@ pax_init(void)
} }
void void
pax_adjust(struct lwp *l, int f) pax_adjust(struct lwp *l, uint32_t f)
{ {
#ifdef PAX_MPROTECT #ifdef PAX_MPROTECT
if (pax_mprotect_enabled) { if (pax_mprotect_enabled) {
if (f & PF_PAXMPROTECT) if (f & ELF_NOTE_PAX_MPROTECT)
proc_setspecific(l->l_proc, pax_mprotect_key, proc_setspecific(l->l_proc, pax_mprotect_key,
PAX_MPROTECT_EXPLICIT_ENABLE); PAX_MPROTECT_EXPLICIT_ENABLE);
if (f & PF_PAXNOMPROTECT) if (f & ELF_NOTE_PAX_NOMPROTECT)
proc_setspecific(l->l_proc, pax_mprotect_key, proc_setspecific(l->l_proc, pax_mprotect_key,
PAX_MPROTECT_EXPLICIT_DISABLE); PAX_MPROTECT_EXPLICIT_DISABLE);
} }
@ -215,11 +215,10 @@ pax_adjust(struct lwp *l, int f)
#ifdef PAX_SEGVGUARD #ifdef PAX_SEGVGUARD
if (pax_segvguard_enabled) { if (pax_segvguard_enabled) {
if (f & PF_PAXGUARD) { if (f & ELF_NOTE_PAX_GUARD)
proc_setspecific(l->l_proc, pax_segvguard_key, proc_setspecific(l->l_proc, pax_segvguard_key,
PAX_SEGVGUARD_EXPLICIT_ENABLE); PAX_SEGVGUARD_EXPLICIT_ENABLE);
} if (f & ELF_NOTE_PAX_NOGUARD)
if (f & PF_PAXNOGUARD)
proc_setspecific(l->l_proc, pax_segvguard_key, proc_setspecific(l->l_proc, pax_segvguard_key,
PAX_SEGVGUARD_EXPLICIT_DISABLE); PAX_SEGVGUARD_EXPLICIT_DISABLE);
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: exec.h,v 1.116 2007/04/22 08:30:02 dsl Exp $ */ /* $NetBSD: exec.h,v 1.117 2007/06/24 20:35:37 christos Exp $ */
/*- /*-
* Copyright (c) 1992, 1993 * Copyright (c) 1992, 1993
@ -201,6 +201,7 @@ struct exec_package {
const struct execsw *ep_esch;/* execsw entry */ const struct execsw *ep_esch;/* execsw entry */
struct vnode *ep_emul_root; /* base of emulation filesystem */ struct vnode *ep_emul_root; /* base of emulation filesystem */
struct vnode *ep_interp; /* vnode of (elf) interpeter */ 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_INDIR 0x0001 /* script handling already done */
#define EXEC_HASFD 0x0002 /* holding a shell script */ #define EXEC_HASFD 0x0002 /* holding a shell script */

View File

@ -1,4 +1,4 @@
/* $NetBSD: exec_elf.h,v 1.89 2006/11/22 15:08:47 riz Exp $ */ /* $NetBSD: exec_elf.h,v 1.90 2007/06/24 20:35:37 christos Exp $ */
/*- /*-
* Copyright (c) 1994 The NetBSD Foundation, Inc. * Copyright (c) 1994 The NetBSD Foundation, Inc.
@ -346,11 +346,6 @@ typedef struct {
#define PF_W 0x2 /* Segment is writable */ #define PF_W 0x2 /* Segment is writable */
#define PF_X 0x1 /* Segment is executable */ #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_MASKOS 0x0ff00000 /* Operating system specific values */
#define PF_MASKPROC 0xf0000000 /* Processor-specific values */ #define PF_MASKPROC 0xf0000000 /* Processor-specific values */
@ -678,6 +673,12 @@ typedef struct {
/* NetBSD-specific note type: Emulation name. desc is emul name string. */ /* NetBSD-specific note type: Emulation name. desc is emul name string. */
#define ELF_NOTE_TYPE_NETBSD_TAG 1 #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 /* 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>. */ section. desc is a tuple of <phnum>(16),<chk-type>(16),<chk-value>. */
#define ELF_NOTE_TYPE_CHECKSUM_TAG 2 #define ELF_NOTE_TYPE_CHECKSUM_TAG 2
@ -686,11 +687,16 @@ typedef struct {
#define ELF_NOTE_CHECKSUM_SHA1 3 #define ELF_NOTE_CHECKSUM_SHA1 3
#define ELF_NOTE_CHECKSUM_SHA256 4 #define ELF_NOTE_CHECKSUM_SHA256 4
/* NetBSD-specific note name and description sizes */ /* NetBSD-specific note type: PaX. There should be 1 NOTE per executable.
#define ELF_NOTE_NETBSD_NAMESZ 7 section. desc is a 32 bit bitmask */
#define ELF_NOTE_NETBSD_DESCSZ 4 #define ELF_NOTE_TYPE_PAX_TAG 3
/* NetBSD-specific note name */ #define ELF_NOTE_PAX_MPROTECT 0x1 /* Force enable Mprotect */
#define ELF_NOTE_NETBSD_NAME "NetBSD\0\0" #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. * NetBSD-specific core file information.

View File

@ -1,4 +1,4 @@
/* $NetBSD: pax.h,v 1.8 2007/02/21 23:00:10 thorpej Exp $ */ /* $NetBSD: pax.h,v 1.9 2007/06/24 20:35:37 christos Exp $ */
/*- /*-
* Copyright (c) 2006 Elad Efrat <elad@NetBSD.org> * Copyright (c) 2006 Elad Efrat <elad@NetBSD.org>
@ -35,7 +35,7 @@
struct lwp; struct lwp;
void pax_init(void); 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 *); void pax_mprotect(struct lwp *, vm_prot_t *, vm_prot_t *);

View File

@ -1,4 +1,4 @@
/* $NetBSD: paxctl.c,v 1.1 2007/01/30 19:40:08 elad Exp $ */ /* $NetBSD: paxctl.c,v 1.2 2007/06/24 20:35:36 christos Exp $ */
/*- /*-
* Copyright (c) 2006 Elad Efrat <elad@NetBSD.org> * Copyright (c) 2006 Elad Efrat <elad@NetBSD.org>
@ -33,7 +33,7 @@
#include <sys/cdefs.h> #include <sys/cdefs.h>
#ifndef lint #ifndef lint
#ifdef __RCSID #ifdef __RCSID
__RCSID("$NetBSD: paxctl.c,v 1.1 2007/01/30 19:40:08 elad Exp $"); __RCSID("$NetBSD: paxctl.c,v 1.2 2007/06/24 20:35:36 christos Exp $");
#endif #endif
#endif /* not lint */ #endif /* not lint */
@ -56,13 +56,17 @@ static int pax_flags_sane(u_long);
static int pax_haveflags(u_long); static int pax_haveflags(u_long);
static void pax_printflags(u_long); static void pax_printflags(u_long);
#ifndef PF_PAXMPROTECT #ifndef ELF_NOTE_TYPE_PAX_TAG
#define PF_PAXMPROTECT 0x08000000 /* NetBSD-specific note type: PaX. There should be 1 NOTE per executable.
#define PF_PAXNOMPROTECT 0x04000000 section. desc is a 32 bit bitmask */
#endif #define ELF_NOTE_TYPE_PAX_TAG 3
#ifndef PF_PAXGUARD #define ELF_NOTE_PAX_MPROTECT 0x1 /* Force enable Mprotect */
#define PF_PAXGUARD 0x02000000 #define ELF_NOTE_PAX_NOMPROTECT 0x2 /* Force disable Mprotect */
#define PF_PAXNOGUARD 0x01000000 #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 #endif
#ifndef __arraycount #ifndef __arraycount
#define __arraycount(a) (sizeof(a) / sizeof(a[0])) #define __arraycount(a) (sizeof(a) / sizeof(a[0]))
@ -74,10 +78,14 @@ static const struct paxflag {
const char *name; const char *name;
int bits; int bits;
} flags[] = { } flags[] = {
{ 'G', "Segvguard, explicit enable", PF_PAXGUARD }, { 'G', "Segvguard, explicit enable",
{ 'g', "Segvguard, explicit disable", PF_PAXNOGUARD }, ELF_NOTE_PAX_GUARD },
{ 'M', "mprotect(2) restrictions, explicit enable", PF_PAXMPROTECT }, { 'g', "Segvguard, explicit disable",
{ 'm', "mprotect(2) restrictions, explicit disable", PF_PAXNOMPROTECT }, 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 static void
@ -156,8 +164,13 @@ main(int argc, char **argv)
Elf32_Phdr h32; Elf32_Phdr h32;
Elf64_Phdr h64; Elf64_Phdr h64;
} p; } p;
union {
Elf32_Nhdr h32;
Elf64_Nhdr h64;
} n;
#define EH(field) (size == 32 ? e.h32.field : e.h64.field) #define EH(field) (size == 32 ? e.h32.field : e.h64.field)
#define PH(field) (size == 32 ? p.h32.field : p.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 { \ #define SPH(field, val) do { \
if (size == 32) \ if (size == 32) \
p.h32.field val; \ p.h32.field val; \
@ -165,6 +178,11 @@ main(int argc, char **argv)
p.h64.field val; \ p.h64.field val; \
} while (/*CONSTCOND*/0) } while (/*CONSTCOND*/0)
#define PHSIZE (size == 32 ? sizeof(p.h32) : sizeof(p.h64)) #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; int size;
char *opt = NULL; char *opt = NULL;
@ -227,41 +245,57 @@ main(int argc, char **argv)
for (i = 0; i < EH(e_phnum); i++) { for (i = 0; i < EH(e_phnum); i++) {
if (pread(fd, &p, PHSIZE, if (pread(fd, &p, PHSIZE,
(off_t)EH(e_phoff) + i * PHSIZE) != 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) if (PH(p_type) != PT_NOTE)
continue; 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; ok = 1;
if (list) { if (list) {
if (!pax_haveflags((u_long)PH(p_flags))) if (!pax_haveflags(pax_tag.flags))
break; break;
if (!pax_flags_sane((u_long)PH(p_flags))) if (!pax_flags_sane(pax_tag.flags))
warnx("Current flags %lx don't make sense", warnx("Current flags %x don't make sense",
(u_long)PH(p_flags)); pax_tag.flags);
(void)printf("PaX flags:\n"); (void)printf("PaX flags:\n");
pax_printflags((u_long)PH(p_flags)); pax_printflags(pax_tag.flags);
flagged = 1; flagged = 1;
break; break;
} }
SPH(p_flags, |= add_flags); pax_tag.flags |= add_flags;
SPH(p_flags, &= ~del_flags); pax_tag.flags &= ~del_flags;
if (!pax_flags_sane((u_long)PH(p_flags))) if (!pax_flags_sane(pax_tag.flags))
errx(EXIT_FAILURE, "New flags %lx don't make sense", errx(EXIT_FAILURE, "New flags %x don't make sense",
(u_long)PH(p_flags)); pax_tag.flags);
if (pwrite(fd, &p, PHSIZE, if (pwrite(fd, &pax_tag, sizeof(pax_tag),
(off_t)EH(e_phoff) + i * PHSIZE) != PHSIZE) (off_t)PH(p_offset) + NHSIZE) != sizeof(pax_tag))
err(EXIT_FAILURE, "Can't modify flags on `%s'", opt); err(EXIT_FAILURE, "Can't modify flags on `%s'", opt);
break; break;
} }
@ -269,7 +303,7 @@ main(int argc, char **argv)
if (!ok) if (!ok)
errx(EXIT_FAILURE, 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) if (list && !flagged)
(void)printf("No PaX flags.\n"); (void)printf("No PaX flags.\n");