[2/2] ARM: fix personality flag propagation across an exec

Message ID alpine.LFD.2.00.1104072251390.28032@xanadu.home
State New
Headers show

Commit Message

Nicolas Pitre April 8, 2011, 2:52 a.m.
Our SET_PERSONALITY() implementation was overwriting all existing
personality flags, including ADDR_NO_RANDOMIZE, making them unavailable
to processes being exec'd after a call to personality() in user space.
This prevents the gdb test suite from running successfully.

Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
---
 arch/arm/kernel/elf.c |   19 +++++++++++++------
 1 files changed, 13 insertions(+), 6 deletions(-)

Comments

Nicolas Pitre April 8, 2011, 1 p.m. | #1
On Fri, 8 Apr 2011, Russell King - ARM Linux wrote:

> On Thu, Apr 07, 2011 at 10:52:53PM -0400, Nicolas Pitre wrote:
> >  	/*
> > +	 * Inherit most personality flags from parent, except for those
> > +	 * we're about to choose.  Beware: PER_LINUX_32BIT carries flag bits
> > +	 * outside of PER_MASK.
> > +	 */
> > +	personality &= ~(PER_MASK | PER_LINUX | PER_LINUX_32BIT);
> 
> PER_LINUX and PER_LINUX_32BIT aren't bitflags - the LSB is a numeric
> personality ID.  So this looks wrong.

From include/linux/personality.h:

enum {
        PER_LINUX =             0x0000,
        PER_LINUX_32BIT =       0x0000 | ADDR_LIMIT_32BIT,
        PER_LINUX_FDPIC =       0x0000 | FDPIC_FUNCPTRS,
        PER_SVR4 =              0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
        [...]

So this is a combination of a personality ID and flag bits.  And the 
only difference between PER_LINUX and PER_LINUX_32BIT is one of those 
flag bits.


Nicolas
Russell King - ARM Linux April 8, 2011, 7:10 p.m. | #2
On Fri, Apr 08, 2011 at 09:00:07AM -0400, Nicolas Pitre wrote:
> On Fri, 8 Apr 2011, Russell King - ARM Linux wrote:
> 
> > On Thu, Apr 07, 2011 at 10:52:53PM -0400, Nicolas Pitre wrote:
> > >  	/*
> > > +	 * Inherit most personality flags from parent, except for those
> > > +	 * we're about to choose.  Beware: PER_LINUX_32BIT carries flag bits
> > > +	 * outside of PER_MASK.
> > > +	 */
> > > +	personality &= ~(PER_MASK | PER_LINUX | PER_LINUX_32BIT);
> > 
> > PER_LINUX and PER_LINUX_32BIT aren't bitflags - the LSB is a numeric
> > personality ID.  So this looks wrong.
> 
> >From include/linux/personality.h:
> 
> enum {
>         PER_LINUX =             0x0000,
>         PER_LINUX_32BIT =       0x0000 | ADDR_LIMIT_32BIT,
>         PER_LINUX_FDPIC =       0x0000 | FDPIC_FUNCPTRS,
>         PER_SVR4 =              0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
>         [...]
> 
> So this is a combination of a personality ID and flag bits.  And the 
> only difference between PER_LINUX and PER_LINUX_32BIT is one of those 
> flag bits.

Yes but its wrong to clear the bitmask using ~PER_LINUX etc.

What you want to be doing is:

	personality &= ~(PER_MASK | ADDR_LIMIT_32BIT);

so you're clearing the LSB being the personality type, and the 32-bit
address limit.
Nicolas Pitre April 8, 2011, 7:50 p.m. | #3
On Fri, 8 Apr 2011, Russell King - ARM Linux wrote:

> On Fri, Apr 08, 2011 at 09:00:07AM -0400, Nicolas Pitre wrote:
> > On Fri, 8 Apr 2011, Russell King - ARM Linux wrote:
> > 
> > > On Thu, Apr 07, 2011 at 10:52:53PM -0400, Nicolas Pitre wrote:
> > > >  	/*
> > > > +	 * Inherit most personality flags from parent, except for those
> > > > +	 * we're about to choose.  Beware: PER_LINUX_32BIT carries flag bits
> > > > +	 * outside of PER_MASK.
> > > > +	 */
> > > > +	personality &= ~(PER_MASK | PER_LINUX | PER_LINUX_32BIT);
> > > 
> > > PER_LINUX and PER_LINUX_32BIT aren't bitflags - the LSB is a numeric
> > > personality ID.  So this looks wrong.
> > 
> > >From include/linux/personality.h:
> > 
> > enum {
> >         PER_LINUX =             0x0000,
> >         PER_LINUX_32BIT =       0x0000 | ADDR_LIMIT_32BIT,
> >         PER_LINUX_FDPIC =       0x0000 | FDPIC_FUNCPTRS,
> >         PER_SVR4 =              0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
> >         [...]
> > 
> > So this is a combination of a personality ID and flag bits.  And the 
> > only difference between PER_LINUX and PER_LINUX_32BIT is one of those 
> > flag bits.
> 
> Yes but its wrong to clear the bitmask using ~PER_LINUX etc.
> 
> What you want to be doing is:
> 
> 	personality &= ~(PER_MASK | ADDR_LIMIT_32BIT);
> 
> so you're clearing the LSB being the personality type, and the 32-bit
> address limit.

Sure.

However, if we're only setting the address limit flag here, wouldn't it 
be better to leave the current personality type as is and only set/clear 
the ADDR_LIMIT_32BIT flag?  Something like:

	unsigned int personality = current->personality;
        if ((eflags & EF_ARM_EABI_MASK) == EF_ARM_EABI_UNKNOWN &&
            (eflags & EF_ARM_APCS_26))
                personality &= ~ADDR_LIMIT_32BIT;
        else
                personality |= ADDR_LIMIT_32BIT;
        set_personality(personality);

Or is the actual personality type not supposed to be inherited?

I also notice that bad_syscall() is broken if extra flags such as 
ADDR_NO_RANDOMIZE are added to the current personality (will send a 
patch for that as well).


Nicolas
Russell King - ARM Linux April 8, 2011, 8:15 p.m. | #4
On Fri, Apr 08, 2011 at 03:50:21PM -0400, Nicolas Pitre wrote:
> However, if we're only setting the address limit flag here, wouldn't it 
> be better to leave the current personality type as is and only set/clear 
> the ADDR_LIMIT_32BIT flag?  Something like:
> 
> 	unsigned int personality = current->personality;
>         if ((eflags & EF_ARM_EABI_MASK) == EF_ARM_EABI_UNKNOWN &&
>             (eflags & EF_ARM_APCS_26))
>                 personality &= ~ADDR_LIMIT_32BIT;
>         else
>                 personality |= ADDR_LIMIT_32BIT;
>         set_personality(personality);
> 
> Or is the actual personality type not supposed to be inherited?
> 
> I also notice that bad_syscall() is broken if extra flags such as 
> ADDR_NO_RANDOMIZE are added to the current personality (will send a 
> patch for that as well).

Many architectures explicitly set a personality type on exec, so that
seems to be the thing to do.  We want it set to a PER_LINUX flavour
as the ELF executables we run tend to be Linux executables.

Also, the ARM kernel doesn't really support anything but PER_LINUX
ELF executables, so it'd be rather meaningless to set it to anything
else here.

So:

	unsigned int personality = current->personality & ~PER_MASK;

	/*
	 * We only support Linux ELF executables, so always set the
	 * personality to LINUX.
	 */
	personality |= PER_LINUX;

	/* APCS-26 is only valid for OABI executables */
	if ((eflags & EF_ARM_EABI_MASK) == EF_ARM_EABI_UNKNOWN &&
	    (eflags & EF_ARM_APCS_26))
		personality &= ~ADDR_LIMIT_32BIT;
	else
		personality |= ADDR_LIMIT_32BIT;

	set_personality(personality);

is probably what we want.
Nicolas Pitre April 8, 2011, 8:44 p.m. | #5
On Fri, 8 Apr 2011, Russell King - ARM Linux wrote:

> On Fri, Apr 08, 2011 at 03:50:21PM -0400, Nicolas Pitre wrote:
> 
> > Or is the actual personality type not supposed to be inherited?
> 
> Many architectures explicitly set a personality type on exec, so that
> seems to be the thing to do.  We want it set to a PER_LINUX flavour
> as the ELF executables we run tend to be Linux executables.
> 
> Also, the ARM kernel doesn't really support anything but PER_LINUX
> ELF executables, so it'd be rather meaningless to set it to anything
> else here.
> 
> So:
> 
> 	unsigned int personality = current->personality & ~PER_MASK;
> 
> 	/*
> 	 * We only support Linux ELF executables, so always set the
> 	 * personality to LINUX.
> 	 */
> 	personality |= PER_LINUX;
> 
> 	/* APCS-26 is only valid for OABI executables */
> 	if ((eflags & EF_ARM_EABI_MASK) == EF_ARM_EABI_UNKNOWN &&
> 	    (eflags & EF_ARM_APCS_26))
> 		personality &= ~ADDR_LIMIT_32BIT;
> 	else
> 		personality |= ADDR_LIMIT_32BIT;
> 
> 	set_personality(personality);
> 
> is probably what we want.

ACK.  Are you committing this directly, or do you still want me to send 
you a patch?


Nicolas

Patch

diff --git a/arch/arm/kernel/elf.c b/arch/arm/kernel/elf.c
index d4a0da1..8524d09 100644
--- a/arch/arm/kernel/elf.c
+++ b/arch/arm/kernel/elf.c
@@ -40,16 +40,23 @@  EXPORT_SYMBOL(elf_check_arch);
 void elf_set_personality(const struct elf32_hdr *x)
 {
 	unsigned int eflags = x->e_flags;
-	unsigned int personality = PER_LINUX_32BIT;
+	unsigned int personality = current->personality;
 
 	/*
+	 * Inherit most personality flags from parent, except for those
+	 * we're about to choose.  Beware: PER_LINUX_32BIT carries flag bits
+	 * outside of PER_MASK.
+	 */
+	personality &= ~(PER_MASK | PER_LINUX | PER_LINUX_32BIT);
+		
+	/*
 	 * APCS-26 is only valid for OABI executables
 	 */
-	if ((eflags & EF_ARM_EABI_MASK) == EF_ARM_EABI_UNKNOWN) {
-		if (eflags & EF_ARM_APCS_26)
-			personality = PER_LINUX;
-	}
-
+	if ((eflags & EF_ARM_EABI_MASK) == EF_ARM_EABI_UNKNOWN &&
+	    (eflags & EF_ARM_APCS_26))
+		personality |= PER_LINUX;
+	else
+		personality |= PER_LINUX_32BIT;
 	set_personality(personality);
 
 	/*