armpaxdep

how to check the hardware support of XN/XI bit support on ARM/MIPS platform


I was verifying the hardware support of XN bit on ARM v6/v7 platform. for this I executed execstack.c on ARM. It is crashing as there is XN bit support for ARM v6/v7. Then I checked the same on MIPS target(34Kc) which does not have XI bit support and hence program must execute normally, but here also this program crashed. Then I removed XN bit code and compiled for ARM. Then also program crashed which should not.

Test Programme /* execstack.c - Tests whether code on the stack can be executed

*/

typedef void (*fptr)(void);

char *testname = "Executable stack                         ";

void itworked( void )
{
      printf( "Vulnerable\n" );
        exit( 1 );
}

void doit( void )
{
       char buf[8192];
        fptr func;
        /* Put a RETN instruction in the buffer */
        buf[0] = '\xc3';
        /* Convert the pointer to a function pointer */
        func = (fptr)buf;
        /* Call the code in the buffer */
        func();
        /* It worked when the function returns */
        itworked();
}

int main( int argc, char *argv[] )
{
       int status;
        printf( "%s: ", testname );
        fflush( stdout );
        if( fork() == 0 ) {
                do_mprotect((unsigned long)argv & ~4095U, 4096, PROT_READ|PROT_WRITE|PROT_EXEC);
                doit();
        } else {
                wait( &status );
                if( WIFEXITED(status) == 0 ) {
                        printf( "Killed\n" );
                        exit( 0 );
               }
        }
        exit( 0 );
}

void itfailed( void )
{
        printf( "Ok\n" );
        exit( 2 );
}

int do_mprotect( const void *addr, size_t len, int prot )
{
        void *ptr;
        int retval;
        /* Allign to a multiple of PAGESIZE, assumed to be a power of two */
        ptr = (char *)(((unsigned long) addr) & ~(PAGESIZE-1));
         retval = mprotect( ptr, len, prot );
        if( retval != 0 && errno == EINVAL ) {
                perror( "could not mprotect():" );
                exit( 1 );
    }
         return retval;
}

/Logs on MIPS target/

On MIPS target the execstack testcase giving below coredump although I assume that XI bit is not supported in MIPS.

VDLinux#> ./execstack

Executable stack[ 53.272000] do_ri() : sending SIGILL to execstack, PID:386

Killed

/Logs on ARM target/

VDLinux#> ./execstack

Executable stack[ 451.784000] execstack: unhandled page fault (11) at 0xbead5860, code 0x80000007 Killed

So I have following questions:

  1. How to verify XN bit support on ARM v6/V7?
  2. How to verify XI bit support on MIPS 34Kc
  3. Where to check XN bit support in Linux Kernel Code.

Thanks, Girish


Solution

  • I have written the below assembly code to test XN bit support on ARM target.

    .text
    .global _start
    _start:
    mov   r0, #1        (output)    
    add   r1, pc, #20   (string)
    mov   r2, #12        strlen(string))
    mov   r7, #4        (syscall number for write)
    svc   0x0
    
    mov   r0, #0        (output)    
    mov   r7, #1        (syscall number for exit)
    svc   0x0
    .asciz  "Hello world\n   "
    

    Generating machine from assembly:

    arm-linux-gnueabi-gcc -c -o arm_hello.o arm_hello.s
    arm-linux-gnueabi-ld arm_hello.o -o arm_hello
    

    Disassembly of section .text:

    root@oss:shellcode_2# arm-linux-gnueabi-objdump -d arm_hello 
    arm_hello :     file format elf32-littlearm
    00008054 <_start>:
    8054:       e3a00001        mov     r0, #1
    8058:       e28f1014        add     r1, pc, #20
    805c:       e3a0200c        mov     r2, #12
    8060:       e3a07004        mov     r7, #4
    8064:       ef000000        svc     0x00000000
    8068:       e3a00000        mov     r0, #0
    806c:       e3a07001        mov     r7, #1
    8070:       ef000000        svc     0x00000000
    8074:       6c6c6548        .word   0x6c6c6548
    8078:       6f77206f        .word   0x6f77206f
    807c:       0a646c72        .word   0x0a646c72
    8080:       00202020        .word   0x00202020 
    

    Final Shell Code in C:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <asm/unistd.h>
    
    typedef void (*fptr) (void);
    
    void
    main ()
    {
      unsigned char hellocode[] = "\x01\x00\xa0\xe3\x14\x10\x8f\xe2"
        "\x0c\x20\xa0\xe3\x04\x70\xa0\xe3"
        "\x00\x00\x00\xef\x00\x00\xa0\xe3"
        "\x01\x70\xa0\xe3\x00\x00\x00\xef" "hello world\n   \0";
    
      unsigned char buffcode[256] __attribute__ ((aligned (32)));
      fptr func;
    
      memcpy (buffcode, hellocode, 49);
    
      /* Convert the pointer to a function pointer */
      func = (fptr) buffcode;
    
      /* flush contents of instruction and/or data cache */
      syscall (__ARM_NR_cacheflush, buffcode, buffcode + 50, 0);
    
      /* Call the code in the buffer */
      (*func) ();
    }
    

    Case 1: When stack is executable:

    Compilation of program:

    root@oss:shellcode_ final# arm-linux-gnueabi-gcc stack.c -z execstack -o stack_RWX
    

    Reading ELF header:

    root@oss:shellcode_final# arm-v7a9v3r0-linux-gnueabi-readelf -l stack_RWX 
    Elf file type is EXEC (Executable file)
    Program Headers:
    Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
    GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4
    

    Running the program: As here stack is executable so XN bit will be cleared (0). And program will run normally.

    ARM_Target#> ./stack_RWX
    hello world
    

    Case 2: When stack is non executable:

    Compilation of program:

    root@oss:shellcode_ final# arm-v7a15v3r1-linux-gnueabi-gcc stack.c -o stack_RW
    

    Reading ELF header:

    root@oss:shellcode_final# arm-linux-gnueabi-readelf -l stack_RW
    Elf file type is EXEC (Executable file)
    Program Headers:
    Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
    GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4
    

    Running the program: As here XN bit is set (it is 1), so we will get segmentation fault in each case.

    ARMtarget#> ./stack_RW
    [   39.092000] stack_RW: unhandled page fault (11) at 0xbeca8760, code 0x8000000f
    [   41.000000] [VDLP COREDUMP] SIGNR:11
    Segmentation fault (core dumped)
    

    Patch for Disabling XN bit in ARM: I have created a patch. In this patch we comment a section of assembly code. This is done in arch/arm/mm/proc-v7.S

    #ifdef CONFIG_XN_SUPPORT
       tst  r1, #L_PTE_XN
       orrne    r3, r3, #PTE_EXT_XN
    #endif
    

    If I deselect CONFIG_XN_SUPPORT option PTE_EXT_XN bit will be always be 0. So all binaries will be executed, whether the stack is executable or not.

    Running the program:

    ARM_Target#> ./stack_RWX
    hello world
    ARM_Target#> ./stack_RW 
    hello world
    

    Conclusion:
    XN bit is supported in Cortex-A15 ARMv7.