[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH 4/4] x86: switch default mapping attributes to non-executable



On 18/05/15 13:47, Jan Beulich wrote:
> Only a very limited subset of mappings need to be done as executable
> ones; in particular the direct mapping should not be executable to
> limit the damage attackers can cause by exploiting security relevant
> bugs.
>
> The EFI change at once includes an adjustment to set NX only when
> supported by the hardware.
>
> Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
>
> --- a/xen/arch/x86/domain.c
> +++ b/xen/arch/x86/domain.c
> @@ -293,7 +293,7 @@ struct vcpu_guest_context *alloc_vcpu_gu
>              free_vcpu_guest_context(NULL);
>              return NULL;
>          }
> -        __set_fixmap(idx - i, page_to_mfn(pg), __PAGE_HYPERVISOR);
> +        __set_fixmap(idx - i, page_to_mfn(pg), __PAGE_HYPERVISOR_RW);
>          per_cpu(vgc_pages[i], cpu) = pg;
>      }
>      return (void *)fix_to_virt(idx);
> --- a/xen/arch/x86/domain_page.c
> +++ b/xen/arch/x86/domain_page.c
> @@ -160,7 +160,7 @@ void *map_domain_page(unsigned long mfn)
>  
>      spin_unlock(&dcache->lock);
>  
> -    l1e_write(&MAPCACHE_L1ENT(idx), l1e_from_pfn(mfn, __PAGE_HYPERVISOR));
> +    l1e_write(&MAPCACHE_L1ENT(idx), l1e_from_pfn(mfn, __PAGE_HYPERVISOR_RW));
>  
>   out:
>      local_irq_restore(flags);
> --- a/xen/arch/x86/mm.c
> +++ b/xen/arch/x86/mm.c
> @@ -4416,7 +4416,7 @@ long set_gdt(struct vcpu *v, 
>      for ( i = 0; i < nr_pages; i++ )
>      {
>          v->arch.pv_vcpu.gdt_frames[i] = frames[i];
> -        l1e_write(&pl1e[i], l1e_from_pfn(frames[i], __PAGE_HYPERVISOR));
> +        l1e_write(&pl1e[i], l1e_from_pfn(frames[i], __PAGE_HYPERVISOR_RW));
>      }
>  
>      xfree(pfns);
> @@ -6004,7 +6004,7 @@ int create_perdomain_mapping(struct doma
>                  if ( !IS_NIL(ppg) )
>                      *ppg++ = pg;
>                  l1tab[l1_table_offset(va)] =
> -                    l1e_from_page(pg, __PAGE_HYPERVISOR | _PAGE_AVAIL0);
> +                    l1e_from_page(pg, __PAGE_HYPERVISOR_RW | _PAGE_AVAIL0);
>                  l2e_add_flags(*pl2e, _PAGE_AVAIL0);
>              }
>              else
> @@ -6133,7 +6133,7 @@ void memguard_init(void)
>          (unsigned long)__va(start),
>          start >> PAGE_SHIFT,
>          (__pa(&_end) + PAGE_SIZE - 1 - start) >> PAGE_SHIFT,
> -        __PAGE_HYPERVISOR|MAP_SMALL_PAGES);
> +        __PAGE_HYPERVISOR_RW|MAP_SMALL_PAGES);
>      BUG_ON(start != xen_phys_start);
>      map_pages_to_xen(
>          XEN_VIRT_START,
> @@ -6146,7 +6146,7 @@ static void __memguard_change_range(void
>  {
>      unsigned long _p = (unsigned long)p;
>      unsigned long _l = (unsigned long)l;
> -    unsigned int flags = __PAGE_HYPERVISOR | MAP_SMALL_PAGES;
> +    unsigned int flags = __PAGE_HYPERVISOR_RW | MAP_SMALL_PAGES;
>  
>      /* Ensure we are dealing with a page-aligned whole number of pages. */
>      ASSERT((_p&~PAGE_MASK) == 0);
> --- a/xen/arch/x86/setup.c
> +++ b/xen/arch/x86/setup.c
> @@ -900,7 +900,7 @@ void __init noreturn __start_xen(unsigne
>              /* The only data mappings to be relocated are in the Xen area. */
>              pl2e = __va(__pa(l2_xenmap));
>              *pl2e++ = l2e_from_pfn(xen_phys_start >> PAGE_SHIFT,
> -                                   PAGE_HYPERVISOR | _PAGE_PSE);
> +                                   PAGE_HYPERVISOR_RWX | _PAGE_PSE);
>              for ( i = 1; i < L2_PAGETABLE_ENTRIES; i++, pl2e++ )
>              {
>                  if ( !(l2e_get_flags(*pl2e) & _PAGE_PRESENT) )
> @@ -1087,7 +1087,7 @@ void __init noreturn __start_xen(unsigne
>              /* This range must not be passed to the boot allocator and
>               * must also not be mapped with _PAGE_GLOBAL. */
>              map_pages_to_xen((unsigned long)__va(map_e), PFN_DOWN(map_e),
> -                             PFN_DOWN(e - map_e), __PAGE_HYPERVISOR);
> +                             PFN_DOWN(e - map_e), __PAGE_HYPERVISOR_RW);
>          }
>          if ( s < map_s )
>          {
> --- a/xen/arch/x86/x86_64/mm.c
> +++ b/xen/arch/x86/x86_64/mm.c
> @@ -895,6 +895,33 @@ void __init subarch_init_memory(void)
>              share_xen_page_with_privileged_guests(page, XENSHARE_readonly);
>          }
>      }
> +
> +    /* Mark low 16Mb of direct map NX if hardware supports it. */
> +    if ( !cpu_has_nx )
> +        return;
> +
> +    v = DIRECTMAP_VIRT_START + (1UL << 20);

What about 0-1MB ?

> +    l3e = l4e_to_l3e(idle_pg_table[l4_table_offset(v)])[l3_table_offset(v)];
> +    ASSERT(l3e_get_flags(l3e) & _PAGE_PRESENT);
> +    do {
> +        l2e = l3e_to_l2e(l3e)[l2_table_offset(v)];
> +        ASSERT(l2e_get_flags(l2e) & _PAGE_PRESENT);
> +        if ( l2e_get_flags(l2e) & _PAGE_PSE )
> +        {
> +            l2e_add_flags(l2e, _PAGE_NX_BIT);
> +            l3e_to_l2e(l3e)[l2_table_offset(v)] = l2e;
> +            v += 1 << L2_PAGETABLE_SHIFT;
> +        }
> +        else
> +        {
> +            l1_pgentry_t l1e = l2e_to_l1e(l2e)[l1_table_offset(v)];
> +
> +            ASSERT(l1e_get_flags(l1e) & _PAGE_PRESENT);
> +            l1e_add_flags(l1e, _PAGE_NX_BIT);
> +            l2e_to_l1e(l2e)[l1_table_offset(v)] = l1e;
> +            v += 1 << L1_PAGETABLE_SHIFT;
> +        }
> +    } while ( v < DIRECTMAP_VIRT_START + (16UL << 20) );

How about just setting l3e.nx and leaving all lower tables alone?

>  }
>  
>  long subarch_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
> @@ -1359,7 +1386,7 @@ int memory_add(unsigned long spfn, unsig
>          if ( i < spfn )
>              i = spfn;
>          ret = map_pages_to_xen((unsigned long)mfn_to_virt(i), i,
> -                               epfn - i, __PAGE_HYPERVISOR);
> +                               epfn - i, __PAGE_HYPERVISOR_RW);
>          if ( ret )
>              return ret;
>      }
> --- a/xen/common/efi/boot.c
> +++ b/xen/common/efi/boot.c
> @@ -1162,7 +1162,7 @@ void __init efi_init_memory(void)
>          EFI_MEMORY_DESCRIPTOR *desc = efi_memmap + i;
>          u64 len = desc->NumberOfPages << EFI_PAGE_SHIFT;
>          unsigned long smfn, emfn;
> -        unsigned int prot = PAGE_HYPERVISOR;
> +        unsigned int prot = PAGE_HYPERVISOR_RWX;
>  
>          printk(XENLOG_INFO " %013" PRIx64 "-%013" PRIx64
>                             " type=%u attr=%016" PRIx64 "\n",
> @@ -1195,7 +1195,7 @@ void __init efi_init_memory(void)
>          if ( desc->Attribute & EFI_MEMORY_WP )
>              prot &= _PAGE_RW;
>          if ( desc->Attribute & EFI_MEMORY_XP )
> -            prot |= _PAGE_NX_BIT;
> +            prot |= _PAGE_NX;
>  
>          if ( pfn_to_pdx(emfn - 1) < (DIRECTMAP_SIZE >> PAGE_SHIFT) &&
>               !(smfn & pfn_hole_mask) &&
> --- a/xen/include/asm-x86/page.h
> +++ b/xen/include/asm-x86/page.h
> @@ -303,7 +303,8 @@ void paging_init(void);
>  #define _PAGE_AVAIL1   _AC(0x400,U)
>  #define _PAGE_AVAIL2   _AC(0x800,U)
>  #define _PAGE_AVAIL    _AC(0xE00,U)
> -#define _PAGE_PSE_PAT _AC(0x1000,U)
> +#define _PAGE_PSE_PAT  _AC(0x1000,U)
> +#define _PAGE_NX       (cpu_has_nx ? _PAGE_NX_BIT : 0)
>  /* non-architectural flags */
>  #define _PAGE_PAGED   0x2000U
>  #define _PAGE_SHARED  0x4000U
> @@ -320,6 +321,9 @@ void paging_init(void);
>  #define _PAGE_GNTTAB   0
>  #endif
>  
> +#define __PAGE_HYPERVISOR_RO      (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_NX)
> +#define __PAGE_HYPERVISOR_RW      (__PAGE_HYPERVISOR_RO | \
> +                                   _PAGE_DIRTY | _PAGE_RW)
>  #define __PAGE_HYPERVISOR_RX      (_PAGE_PRESENT | _PAGE_ACCESSED)
>  #define __PAGE_HYPERVISOR         (__PAGE_HYPERVISOR_RX | \
>                                     _PAGE_DIRTY | _PAGE_RW)
> --- a/xen/include/asm-x86/x86_64/page.h
> +++ b/xen/include/asm-x86/x86_64/page.h
> @@ -147,9 +147,20 @@ typedef l4_pgentry_t root_pgentry_t;
>   */
>  #define _PAGE_GUEST_KERNEL (1U<<12)
>  
> -#define PAGE_HYPERVISOR         (__PAGE_HYPERVISOR         | _PAGE_GLOBAL)
> +#define PAGE_HYPERVISOR_RO      (__PAGE_HYPERVISOR_RO      | _PAGE_GLOBAL)
> +#define PAGE_HYPERVISOR_RW      (__PAGE_HYPERVISOR_RW      | _PAGE_GLOBAL)
>  #define PAGE_HYPERVISOR_RX      (__PAGE_HYPERVISOR_RX      | _PAGE_GLOBAL)
> -#define PAGE_HYPERVISOR_NOCACHE (__PAGE_HYPERVISOR_NOCACHE | _PAGE_GLOBAL)
> +#define PAGE_HYPERVISOR_RWX     (__PAGE_HYPERVISOR         | _PAGE_GLOBAL)
> +
> +#ifdef __ASSEMBLY__
> +/* Dependency on NX being available can't be expressed. */
> +# define PAGE_HYPERVISOR         PAGE_HYPERVISOR_RWX
> +# define PAGE_HYPERVISOR_NOCACHE (__PAGE_HYPERVISOR_NOCACHE | _PAGE_GLOBAL)
> +#else
> +# define PAGE_HYPERVISOR         PAGE_HYPERVISOR_RW
> +# define PAGE_HYPERVISOR_NOCACHE (__PAGE_HYPERVISOR_NOCACHE | \
> +                                  _PAGE_GLOBAL | _PAGE_NX)
> +#endif

This patch is clearly an improvement, so my comments can possibly be
deferred to subsequent patches.

After boot, I can't think of a legitimate reason for Xen to have a RWX
mapping.  As such, leaving __PAGE_HYPERVISOR around constitutes a risk
that RWX mappings will be used.  It would be nice if we could remove all
constants (which aren't BOOT_*) which could lead to accidental use of
RWX mappings on NX-enabled hardware.

I think we also want a warning on boot if we find NX unavailable.  The
only 64bit capable CPUs which do not support NX are now 11 years old,
and I don't expect many of those to be running Xen these days.  However,
given that "disable NX" is a common BIOS option for compatibility
reasons, we should press people to alter their settings if possible.

~Andrew

>  
>  #endif /* __X86_64_PAGE_H__ */
>  
>
>


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.