Blame SOURCES/0273-nx-set-attrs-in-our-kernel-loaders.patch

1c6ba0
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
1c6ba0
From: Peter Jones <pjones@redhat.com>
1c6ba0
Date: Tue, 22 Mar 2022 10:57:07 -0400
1c6ba0
Subject: [PATCH] nx: set attrs in our kernel loaders
1c6ba0
1c6ba0
For NX, our kernel loaders need to set write and execute page
1c6ba0
permissions on allocated pages and the stack.
1c6ba0
1c6ba0
This patch adds those calls.
1c6ba0
1c6ba0
Signed-off-by: Peter Jones <pjones@redhat.com>
1c6ba0
[rharwood: fix aarch64 callsites]
1c6ba0
(cherry-picked from commit a9f79a997f01a83b36cdfa89ef2e72ac2a17c06c)
1c6ba0
[rharwood: uninitialized stack_attrs, double verification]
1c6ba0
(cherry picked from commit f9ac7ceef8a35406893c0cb9a4a8b2e5442bbb1d)
1c6ba0
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
1c6ba0
---
1c6ba0
 grub-core/kern/efi/mm.c            |  78 ++++++++++++++++++
1c6ba0
 grub-core/loader/arm64/linux.c     |  16 +++-
1c6ba0
 grub-core/loader/arm64/xen_boot.c  |   4 +-
1c6ba0
 grub-core/loader/efi/chainloader.c |  11 +++
1c6ba0
 grub-core/loader/efi/linux.c       | 164 ++++++++++++++++++++++++++++++++++++-
1c6ba0
 grub-core/loader/i386/efi/linux.c  |  26 +++++-
1c6ba0
 grub-core/loader/i386/linux.c      |   5 ++
1c6ba0
 include/grub/efi/efi.h             |   6 +-
1c6ba0
 include/grub/efi/linux.h           |  17 +++-
1c6ba0
 include/grub/efi/pe32.h            |   2 +
1c6ba0
 10 files changed, 314 insertions(+), 15 deletions(-)
1c6ba0
1c6ba0
diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
1c6ba0
index 2c33758ed7..88364d764c 100644
1c6ba0
--- a/grub-core/kern/efi/mm.c
1c6ba0
+++ b/grub-core/kern/efi/mm.c
1c6ba0
@@ -610,6 +610,82 @@ print_memory_map (grub_efi_memory_descriptor_t *memory_map,
1c6ba0
 }
1c6ba0
 #endif
1c6ba0
 
1c6ba0
+grub_addr_t grub_stack_addr = (grub_addr_t)-1ll;
1c6ba0
+grub_size_t grub_stack_size = 0;
1c6ba0
+
1c6ba0
+static void
1c6ba0
+grub_nx_init (void)
1c6ba0
+{
1c6ba0
+  grub_uint64_t attrs, stack_attrs;
1c6ba0
+  grub_err_t err;
1c6ba0
+  grub_addr_t stack_current, stack_end;
1c6ba0
+  const grub_uint64_t page_size = 4096;
1c6ba0
+  const grub_uint64_t page_mask = ~(page_size - 1);
1c6ba0
+
1c6ba0
+  /*
1c6ba0
+   * These are to confirm that the flags are working as expected when
1c6ba0
+   * debugging.
1c6ba0
+   */
1c6ba0
+  attrs = 0;
1c6ba0
+  stack_current = (grub_addr_t)grub_nx_init & page_mask;
1c6ba0
+  err = grub_get_mem_attrs (stack_current, page_size, &attrs);
1c6ba0
+  if (err)
1c6ba0
+    {
1c6ba0
+      grub_dprintf ("nx",
1c6ba0
+		    "grub_get_mem_attrs(0x%"PRIxGRUB_UINT64_T", ...) -> 0x%x\n",
1c6ba0
+		    stack_current, err);
1c6ba0
+      grub_error_pop ();
1c6ba0
+    }
1c6ba0
+  else
1c6ba0
+    grub_dprintf ("nx", "page attrs for grub_nx_init (%p) are %c%c%c\n",
1c6ba0
+		  grub_dl_load_core,
1c6ba0
+		  (attrs & GRUB_MEM_ATTR_R) ? 'r' : '-',
1c6ba0
+		  (attrs & GRUB_MEM_ATTR_R) ? 'w' : '-',
1c6ba0
+		  (attrs & GRUB_MEM_ATTR_R) ? 'x' : '-');
1c6ba0
+
1c6ba0
+  stack_current = (grub_addr_t)&stack_current & page_mask;
1c6ba0
+  err = grub_get_mem_attrs (stack_current, page_size, &stack_attrs);
1c6ba0
+  if (err)
1c6ba0
+    {
1c6ba0
+      grub_dprintf ("nx",
1c6ba0
+		    "grub_get_mem_attrs(0x%"PRIxGRUB_UINT64_T", ...) -> 0x%x\n",
1c6ba0
+		    stack_current, err);
1c6ba0
+      grub_error_pop ();
1c6ba0
+    }
1c6ba0
+  else
1c6ba0
+    {
1c6ba0
+      attrs = stack_attrs;
1c6ba0
+      grub_dprintf ("nx", "page attrs for stack (%p) are %c%c%c\n",
1c6ba0
+                    &attrs,
1c6ba0
+                    (attrs & GRUB_MEM_ATTR_R) ? 'r' : '-',
1c6ba0
+                    (attrs & GRUB_MEM_ATTR_R) ? 'w' : '-',
1c6ba0
+                    (attrs & GRUB_MEM_ATTR_R) ? 'x' : '-');
1c6ba0
+    }
1c6ba0
+
1c6ba0
+  for (stack_end = stack_current + page_size ;
1c6ba0
+       !(attrs & GRUB_MEM_ATTR_R);
1c6ba0
+       stack_end += page_size)
1c6ba0
+    {
1c6ba0
+      err = grub_get_mem_attrs (stack_current, page_size, &attrs);
1c6ba0
+      if (err)
1c6ba0
+	{
1c6ba0
+	  grub_dprintf ("nx",
1c6ba0
+			"grub_get_mem_attrs(0x%"PRIxGRUB_UINT64_T", ...) -> 0x%x\n",
1c6ba0
+			stack_current, err);
1c6ba0
+	  grub_error_pop ();
1c6ba0
+	  break;
1c6ba0
+	}
1c6ba0
+    }
1c6ba0
+  if (stack_end > stack_current)
1c6ba0
+    {
1c6ba0
+      grub_stack_addr = stack_current;
1c6ba0
+      grub_stack_size = stack_end - stack_current;
1c6ba0
+      grub_dprintf ("nx",
1c6ba0
+		    "detected stack from 0x%"PRIxGRUB_ADDR" to 0x%"PRIxGRUB_ADDR"\n",
1c6ba0
+		    grub_stack_addr, grub_stack_addr + grub_stack_size - 1);
1c6ba0
+    }
1c6ba0
+}
1c6ba0
+
1c6ba0
 void
1c6ba0
 grub_efi_mm_init (void)
1c6ba0
 {
1c6ba0
@@ -623,6 +699,8 @@ grub_efi_mm_init (void)
1c6ba0
   grub_efi_uint64_t required_pages;
1c6ba0
   int mm_status;
1c6ba0
 
1c6ba0
+  grub_nx_init ();
1c6ba0
+
1c6ba0
   /* Prepare a memory region to store two memory maps.  */
1c6ba0
   memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
1c6ba0
   if (! memory_map)
1c6ba0
diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
1c6ba0
index bcc6ef46e9..70db5a6e0b 100644
1c6ba0
--- a/grub-core/loader/arm64/linux.c
1c6ba0
+++ b/grub-core/loader/arm64/linux.c
1c6ba0
@@ -173,7 +173,8 @@ free_params (void)
1c6ba0
 }
1c6ba0
 
1c6ba0
 grub_err_t
1c6ba0
-grub_arch_efi_linux_boot_image (grub_addr_t addr, char *args)
1c6ba0
+grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args,
1c6ba0
+				int nx_supported)
1c6ba0
 {
1c6ba0
   grub_err_t retval;
1c6ba0
 
1c6ba0
@@ -183,7 +184,8 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, char *args)
1c6ba0
 
1c6ba0
   grub_dprintf ("linux", "linux command line: '%s'\n", args);
1c6ba0
 
1c6ba0
-  retval = grub_efi_linux_boot ((char *)addr, handover_offset, (void *)addr);
1c6ba0
+  retval = grub_efi_linux_boot (addr, size, handover_offset,
1c6ba0
+				(void *)addr, nx_supported);
1c6ba0
 
1c6ba0
   /* Never reached... */
1c6ba0
   free_params();
1c6ba0
@@ -193,7 +195,10 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, char *args)
1c6ba0
 static grub_err_t
1c6ba0
 grub_linux_boot (void)
1c6ba0
 {
1c6ba0
-  return (grub_arch_efi_linux_boot_image((grub_addr_t)kernel_addr, linux_args));
1c6ba0
+  return grub_arch_efi_linux_boot_image((grub_addr_t)kernel_addr,
1c6ba0
+					(grub_size_t)kernel_size,
1c6ba0
+					linux_args,
1c6ba0
+					0);
1c6ba0
 }
1c6ba0
 
1c6ba0
 static grub_err_t
1c6ba0
@@ -342,6 +347,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
1c6ba0
   grub_uint32_t align;
1c6ba0
   void *kernel = NULL;
1c6ba0
   int rc;
1c6ba0
+  int nx_supported = 1;
1c6ba0
 
1c6ba0
   grub_dl_ref (my_mod);
1c6ba0
 
1c6ba0
@@ -389,6 +395,10 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
1c6ba0
   grub_dprintf ("linux", "kernel entry offset : %d\n", handover_offset);
1c6ba0
   grub_dprintf ("linux", "kernel alignment    : 0x%x\n", align);
1c6ba0
 
1c6ba0
+  err = grub_efi_check_nx_image_support((grub_addr_t)kernel, filelen, &nx_supported);
1c6ba0
+  if (err != GRUB_ERR_NONE)
1c6ba0
+    goto fail;
1c6ba0
+
1c6ba0
   grub_loader_unset();
1c6ba0
 
1c6ba0
   kernel_alloc_pages = GRUB_EFI_BYTES_TO_PAGES (kernel_size + align - 1);
1c6ba0
diff --git a/grub-core/loader/arm64/xen_boot.c b/grub-core/loader/arm64/xen_boot.c
1c6ba0
index d9b7a9ba40..6e7e920416 100644
1c6ba0
--- a/grub-core/loader/arm64/xen_boot.c
1c6ba0
+++ b/grub-core/loader/arm64/xen_boot.c
1c6ba0
@@ -266,7 +266,9 @@ xen_boot (void)
1c6ba0
     return err;
1c6ba0
 
1c6ba0
   return grub_arch_efi_linux_boot_image (xen_hypervisor->start,
1c6ba0
-					  xen_hypervisor->cmdline);
1c6ba0
+                                         xen_hypervisor->size,
1c6ba0
+                                         xen_hypervisor->cmdline,
1c6ba0
+                                         0);
1c6ba0
 }
1c6ba0
 
1c6ba0
 static void
1c6ba0
diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
1c6ba0
index 8ef508beca..6ac69f0f59 100644
1c6ba0
--- a/grub-core/loader/efi/chainloader.c
1c6ba0
+++ b/grub-core/loader/efi/chainloader.c
1c6ba0
@@ -1071,6 +1071,17 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
1c6ba0
       goto fail;
1c6ba0
     }
1c6ba0
 
1c6ba0
+  /*
1c6ba0
+   * The OS kernel is going to set its own permissions when it takes over
1c6ba0
+   * paging a few million instructions from now, and load_image() will set up
1c6ba0
+   * anything that's needed based on the section headers, so there's no point
1c6ba0
+   * in doing anything but clearing the protection bits here.
1c6ba0
+   */
1c6ba0
+  grub_dprintf("nx", "setting attributes for %p (%lu bytes) to %llx\n",
1c6ba0
+	       (void *)(grub_addr_t)address, fsize, 0llu);
1c6ba0
+  grub_update_mem_attrs (address, fsize,
1c6ba0
+			 GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_W|GRUB_MEM_ATTR_X, 0);
1c6ba0
+
1c6ba0
 #if defined (__i386__) || defined (__x86_64__)
1c6ba0
   if (fsize >= (grub_ssize_t) sizeof (struct grub_macho_fat_header))
1c6ba0
     {
1c6ba0
diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c
1c6ba0
index 9260731c10..dcc9ea40ea 100644
1c6ba0
--- a/grub-core/loader/efi/linux.c
1c6ba0
+++ b/grub-core/loader/efi/linux.c
1c6ba0
@@ -66,16 +66,127 @@ grub_linuxefi_secure_validate (void *data, grub_uint32_t size)
1c6ba0
 
1c6ba0
 #pragma GCC diagnostic push
1c6ba0
 #pragma GCC diagnostic ignored "-Wcast-align"
1c6ba0
+#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
1c6ba0
+
1c6ba0
+grub_err_t
1c6ba0
+grub_efi_check_nx_image_support (grub_addr_t kernel_addr,
1c6ba0
+				 grub_size_t kernel_size,
1c6ba0
+				 int *nx_supported)
1c6ba0
+{
1c6ba0
+  struct grub_dos_header *doshdr;
1c6ba0
+  grub_size_t sz = sizeof (*doshdr);
1c6ba0
+
1c6ba0
+  struct grub_pe32_header_32 *pe32;
1c6ba0
+  struct grub_pe32_header_64 *pe64;
1c6ba0
+
1c6ba0
+  int image_is_compatible = 0;
1c6ba0
+  int is_64_bit;
1c6ba0
+
1c6ba0
+  if (kernel_size < sz)
1c6ba0
+    return grub_error (GRUB_ERR_BAD_OS, N_("kernel is too small"));
1c6ba0
+
1c6ba0
+  doshdr = (void *)kernel_addr;
1c6ba0
+
1c6ba0
+  if ((doshdr->magic & 0xffff) != GRUB_DOS_MAGIC)
1c6ba0
+    return grub_error (GRUB_ERR_BAD_OS, N_("kernel DOS magic is invalid"));
1c6ba0
+
1c6ba0
+  sz = doshdr->lfanew + sizeof (*pe32);
1c6ba0
+  if (kernel_size < sz)
1c6ba0
+    return grub_error (GRUB_ERR_BAD_OS, N_("kernel is too small"));
1c6ba0
+
1c6ba0
+  pe32 = (struct grub_pe32_header_32 *)(kernel_addr + doshdr->lfanew);
1c6ba0
+  pe64 = (struct grub_pe32_header_64 *)pe32;
1c6ba0
+
1c6ba0
+  if (grub_memcmp (pe32->signature, GRUB_PE32_SIGNATURE,
1c6ba0
+		   GRUB_PE32_SIGNATURE_SIZE) != 0)
1c6ba0
+    return grub_error (GRUB_ERR_BAD_OS, N_("kernel PE magic is invalid"));
1c6ba0
+
1c6ba0
+  switch (pe32->coff_header.machine)
1c6ba0
+    {
1c6ba0
+    case GRUB_PE32_MACHINE_ARMTHUMB_MIXED:
1c6ba0
+    case GRUB_PE32_MACHINE_I386:
1c6ba0
+    case GRUB_PE32_MACHINE_RISCV32:
1c6ba0
+      is_64_bit = 0;
1c6ba0
+      break;
1c6ba0
+    case GRUB_PE32_MACHINE_ARM64:
1c6ba0
+    case GRUB_PE32_MACHINE_IA64:
1c6ba0
+    case GRUB_PE32_MACHINE_RISCV64:
1c6ba0
+    case GRUB_PE32_MACHINE_X86_64:
1c6ba0
+      is_64_bit = 1;
1c6ba0
+      break;
1c6ba0
+    default:
1c6ba0
+      return grub_error (GRUB_ERR_BAD_OS, N_("PE machine type 0x%04hx unknown"),
1c6ba0
+			 pe32->coff_header.machine);
1c6ba0
+    }
1c6ba0
+
1c6ba0
+  if (is_64_bit)
1c6ba0
+    {
1c6ba0
+      sz = doshdr->lfanew + sizeof (*pe64);
1c6ba0
+      if (kernel_size < sz)
1c6ba0
+	return grub_error (GRUB_ERR_BAD_OS, N_("kernel is too small"));
1c6ba0
+
1c6ba0
+      if (pe64->optional_header.dll_characteristics & GRUB_PE32_NX_COMPAT)
1c6ba0
+	image_is_compatible = 1;
1c6ba0
+    }
1c6ba0
+  else
1c6ba0
+    {
1c6ba0
+      if (pe32->optional_header.dll_characteristics & GRUB_PE32_NX_COMPAT)
1c6ba0
+	image_is_compatible = 1;
1c6ba0
+    }
1c6ba0
+
1c6ba0
+  *nx_supported = image_is_compatible;
1c6ba0
+  return GRUB_ERR_NONE;
1c6ba0
+}
1c6ba0
+
1c6ba0
+grub_err_t
1c6ba0
+grub_efi_check_nx_required (int *nx_required)
1c6ba0
+{
1c6ba0
+  grub_efi_status_t status;
1c6ba0
+  grub_efi_guid_t guid = GRUB_EFI_SHIM_LOCK_GUID;
1c6ba0
+  grub_size_t mok_policy_sz = 0;
1c6ba0
+  char *mok_policy = NULL;
1c6ba0
+  grub_uint32_t mok_policy_attrs = 0;
1c6ba0
+
1c6ba0
+  status = grub_efi_get_variable_with_attributes ("MokPolicy", &guid,
1c6ba0
+						  &mok_policy_sz,
1c6ba0
+						  (void **)&mok_policy,
1c6ba0
+						  &mok_policy_attrs);
1c6ba0
+  if (status == GRUB_EFI_NOT_FOUND ||
1c6ba0
+      mok_policy_sz == 0 ||
1c6ba0
+      mok_policy == NULL)
1c6ba0
+    {
1c6ba0
+      *nx_required = 0;
1c6ba0
+      return GRUB_ERR_NONE;
1c6ba0
+    }
1c6ba0
+
1c6ba0
+  *nx_required = 0;
1c6ba0
+  if (mok_policy_sz < 1 ||
1c6ba0
+      mok_policy_attrs != (GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS |
1c6ba0
+			   GRUB_EFI_VARIABLE_RUNTIME_ACCESS) ||
1c6ba0
+      (mok_policy[mok_policy_sz-1] & GRUB_MOK_POLICY_NX_REQUIRED))
1c6ba0
+    *nx_required = 1;
1c6ba0
+
1c6ba0
+  return GRUB_ERR_NONE;
1c6ba0
+}
1c6ba0
 
1c6ba0
 typedef void (*handover_func) (void *, grub_efi_system_table_t *, void *);
1c6ba0
 
1c6ba0
 grub_err_t
1c6ba0
-grub_efi_linux_boot (void *kernel_addr, grub_off_t handover_offset,
1c6ba0
-		     void *kernel_params)
1c6ba0
+grub_efi_linux_boot (grub_addr_t kernel_addr, grub_size_t kernel_size,
1c6ba0
+		     grub_off_t handover_offset, void *kernel_params,
1c6ba0
+		     int nx_supported)
1c6ba0
 {
1c6ba0
   grub_efi_loaded_image_t *loaded_image = NULL;
1c6ba0
   handover_func hf;
1c6ba0
   int offset = 0;
1c6ba0
+  grub_uint64_t stack_set_attrs = GRUB_MEM_ATTR_R |
1c6ba0
+				  GRUB_MEM_ATTR_W |
1c6ba0
+				  GRUB_MEM_ATTR_X;
1c6ba0
+  grub_uint64_t stack_clear_attrs = 0;
1c6ba0
+  grub_uint64_t kernel_set_attrs = stack_set_attrs;
1c6ba0
+  grub_uint64_t kernel_clear_attrs = stack_clear_attrs;
1c6ba0
+  grub_uint64_t attrs;
1c6ba0
+  int nx_required = 0;
1c6ba0
 
1c6ba0
 #ifdef __x86_64__
1c6ba0
   offset = 512;
1c6ba0
@@ -88,12 +199,57 @@ grub_efi_linux_boot (void *kernel_addr, grub_off_t handover_offset,
1c6ba0
    */
1c6ba0
   loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle);
1c6ba0
   if (loaded_image)
1c6ba0
-    loaded_image->image_base = kernel_addr;
1c6ba0
+    loaded_image->image_base = (void *)kernel_addr;
1c6ba0
   else
1c6ba0
     grub_dprintf ("linux", "Loaded Image base address could not be set\n");
1c6ba0
 
1c6ba0
   grub_dprintf ("linux", "kernel_addr: %p handover_offset: %p params: %p\n",
1c6ba0
-		kernel_addr, (void *)(grub_efi_uintn_t)handover_offset, kernel_params);
1c6ba0
+		(void *)kernel_addr, (void *)handover_offset, kernel_params);
1c6ba0
+
1c6ba0
+
1c6ba0
+  if (nx_required && !nx_supported)
1c6ba0
+    return grub_error (GRUB_ERR_BAD_OS, N_("kernel does not support NX loading required by policy"));
1c6ba0
+
1c6ba0
+  if (nx_supported)
1c6ba0
+    {
1c6ba0
+      kernel_set_attrs &= ~GRUB_MEM_ATTR_W;
1c6ba0
+      kernel_clear_attrs |= GRUB_MEM_ATTR_W;
1c6ba0
+      stack_set_attrs &= ~GRUB_MEM_ATTR_X;
1c6ba0
+      stack_clear_attrs |= GRUB_MEM_ATTR_X;
1c6ba0
+    }
1c6ba0
+
1c6ba0
+  grub_dprintf ("nx", "Setting attributes for 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" to r%cx\n",
1c6ba0
+		    kernel_addr, kernel_addr + kernel_size - 1,
1c6ba0
+		    (kernel_set_attrs & GRUB_MEM_ATTR_W) ? 'w' : '-');
1c6ba0
+  grub_update_mem_attrs (kernel_addr, kernel_size,
1c6ba0
+			 kernel_set_attrs, kernel_clear_attrs);
1c6ba0
+
1c6ba0
+  grub_get_mem_attrs (kernel_addr, 4096, &attrs);
1c6ba0
+  grub_dprintf ("nx", "permissions for 0x%"PRIxGRUB_ADDR" are %s%s%s\n",
1c6ba0
+		(grub_addr_t)kernel_addr,
1c6ba0
+		(attrs & GRUB_MEM_ATTR_R) ? "r" : "-",
1c6ba0
+		(attrs & GRUB_MEM_ATTR_W) ? "w" : "-",
1c6ba0
+		(attrs & GRUB_MEM_ATTR_X) ? "x" : "-");
1c6ba0
+  if (grub_stack_addr != (grub_addr_t)-1ll)
1c6ba0
+    {
1c6ba0
+      grub_dprintf ("nx", "Setting attributes for stack at 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" to rw%c\n",
1c6ba0
+		    grub_stack_addr, grub_stack_addr + grub_stack_size - 1,
1c6ba0
+		    (stack_set_attrs & GRUB_MEM_ATTR_X) ? 'x' : '-');
1c6ba0
+      grub_update_mem_attrs (grub_stack_addr, grub_stack_size,
1c6ba0
+			     stack_set_attrs, stack_clear_attrs);
1c6ba0
+
1c6ba0
+      grub_get_mem_attrs (grub_stack_addr, 4096, &attrs);
1c6ba0
+      grub_dprintf ("nx", "permissions for 0x%"PRIxGRUB_ADDR" are %s%s%s\n",
1c6ba0
+		    grub_stack_addr,
1c6ba0
+		    (attrs & GRUB_MEM_ATTR_R) ? "r" : "-",
1c6ba0
+		    (attrs & GRUB_MEM_ATTR_W) ? "w" : "-",
1c6ba0
+		    (attrs & GRUB_MEM_ATTR_X) ? "x" : "-");
1c6ba0
+    }
1c6ba0
+
1c6ba0
+#if defined(__i386__) || defined(__x86_64__)
1c6ba0
+  asm volatile ("cli");
1c6ba0
+#endif
1c6ba0
+
1c6ba0
   hf = (handover_func)((char *)kernel_addr + handover_offset + offset);
1c6ba0
   hf (grub_efi_image_handle, grub_efi_system_table, kernel_params);
1c6ba0
 
1c6ba0
diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
1c6ba0
index b832c85728..dc98077378 100644
1c6ba0
--- a/grub-core/loader/i386/efi/linux.c
1c6ba0
+++ b/grub-core/loader/i386/efi/linux.c
1c6ba0
@@ -45,7 +45,7 @@ struct grub_linuxefi_context {
1c6ba0
   grub_uint32_t handover_offset;
1c6ba0
   struct linux_kernel_params *params;
1c6ba0
   char *cmdline;
1c6ba0
-
1c6ba0
+  int nx_supported;
1c6ba0
   void *initrd_mem;
1c6ba0
 };
1c6ba0
 
1c6ba0
@@ -111,13 +111,19 @@ kernel_alloc(grub_efi_uintn_t size,
1c6ba0
       pages = BYTES_TO_PAGES(size);
1c6ba0
       grub_dprintf ("linux", "Trying to allocate %lu pages from %p\n",
1c6ba0
 		    (unsigned long)pages, (void *)(unsigned long)max);
1c6ba0
+      size = pages * GRUB_EFI_PAGE_SIZE;
1c6ba0
 
1c6ba0
       prev_max = max;
1c6ba0
       addr = grub_efi_allocate_pages_real (max, pages,
1c6ba0
 					   max_addresses[i].alloc_type,
1c6ba0
 					   memtype);
1c6ba0
       if (addr)
1c6ba0
-	grub_dprintf ("linux", "Allocated at %p\n", addr);
1c6ba0
+	{
1c6ba0
+	  grub_dprintf ("linux", "Allocated at %p\n", addr);
1c6ba0
+	  grub_update_mem_attrs ((grub_addr_t)addr, size,
1c6ba0
+				 GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_W,
1c6ba0
+				 GRUB_MEM_ATTR_X);
1c6ba0
+	}
1c6ba0
     }
1c6ba0
 
1c6ba0
   while (grub_error_pop ())
1c6ba0
@@ -138,9 +144,11 @@ grub_linuxefi_boot (void *data)
1c6ba0
 
1c6ba0
   asm volatile ("cli");
1c6ba0
 
1c6ba0
-  return grub_efi_linux_boot ((char *)context->kernel_mem,
1c6ba0
+  return grub_efi_linux_boot ((grub_addr_t)context->kernel_mem,
1c6ba0
+			      context->kernel_size,
1c6ba0
 			      context->handover_offset,
1c6ba0
-			      context->params);
1c6ba0
+			      context->params,
1c6ba0
+			      context->nx_supported);
1c6ba0
 }
1c6ba0
 
1c6ba0
 static grub_err_t
1c6ba0
@@ -306,7 +314,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
1c6ba0
   grub_uint32_t handover_offset;
1c6ba0
   struct linux_kernel_params *params = 0;
1c6ba0
   char *cmdline = 0;
1c6ba0
+  int nx_supported = 1;
1c6ba0
   struct grub_linuxefi_context *context = 0;
1c6ba0
+  grub_err_t err;
1c6ba0
 
1c6ba0
   grub_dl_ref (my_mod);
1c6ba0
 
1c6ba0
@@ -347,6 +357,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
1c6ba0
 	}
1c6ba0
     }
1c6ba0
 
1c6ba0
+  err = grub_efi_check_nx_image_support ((grub_addr_t)kernel, filelen,
1c6ba0
+					 &nx_supported);
1c6ba0
+  if (err != GRUB_ERR_NONE)
1c6ba0
+    return err;
1c6ba0
+  grub_dprintf ("linux", "nx is%s supported by this kernel\n",
1c6ba0
+		nx_supported ? "" : " not");
1c6ba0
+
1c6ba0
   lh = (struct linux_i386_kernel_header *)kernel;
1c6ba0
   grub_dprintf ("linux", "original lh is at %p\n", kernel);
1c6ba0
 
1c6ba0
@@ -511,6 +528,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
1c6ba0
   context->handover_offset = handover_offset;
1c6ba0
   context->params = params;
1c6ba0
   context->cmdline = cmdline;
1c6ba0
+  context->nx_supported = nx_supported;
1c6ba0
 
1c6ba0
   grub_loader_set_ex (grub_linuxefi_boot, grub_linuxefi_unload, context, 0);
1c6ba0
 
1c6ba0
diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c
1c6ba0
index 4aeb0e4b9a..3c1ff64763 100644
1c6ba0
--- a/grub-core/loader/i386/linux.c
1c6ba0
+++ b/grub-core/loader/i386/linux.c
1c6ba0
@@ -805,6 +805,11 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
1c6ba0
       kernel_offset += len;
1c6ba0
     }
1c6ba0
 
1c6ba0
+  grub_dprintf("efi", "setting attributes for %p (%zu bytes) to +rw-x\n",
1c6ba0
+	       &linux_params, sizeof (lh) + len);
1c6ba0
+  grub_update_mem_attrs ((grub_addr_t)&linux_params, sizeof (lh) + len,
1c6ba0
+			 GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_W, GRUB_MEM_ATTR_X);
1c6ba0
+
1c6ba0
   linux_params.code32_start = prot_mode_target + lh.code32_start - GRUB_LINUX_BZIMAGE_ADDR;
1c6ba0
   linux_params.kernel_alignment = (1 << align);
1c6ba0
   linux_params.ps_mouse = linux_params.padding11 = 0;
1c6ba0
diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
1c6ba0
index 34825c4adc..449e55269f 100644
1c6ba0
--- a/include/grub/efi/efi.h
1c6ba0
+++ b/include/grub/efi/efi.h
1c6ba0
@@ -140,12 +140,16 @@ extern void (*EXPORT_VAR(grub_efi_net_config)) (grub_efi_handle_t hnd,
1c6ba0
 						char **device,
1c6ba0
 						char **path);
1c6ba0
 
1c6ba0
+extern grub_addr_t EXPORT_VAR(grub_stack_addr);
1c6ba0
+extern grub_size_t EXPORT_VAR(grub_stack_size);
1c6ba0
+
1c6ba0
 #if defined(__arm__) || defined(__aarch64__) || defined(__riscv)
1c6ba0
 void *EXPORT_FUNC(grub_efi_get_firmware_fdt)(void);
1c6ba0
 grub_err_t EXPORT_FUNC(grub_efi_get_ram_base)(grub_addr_t *);
1c6ba0
 #include <grub/cpu/linux.h>
1c6ba0
 grub_err_t grub_arch_efi_linux_check_image(struct linux_arch_kernel_header *lh);
1c6ba0
-grub_err_t grub_arch_efi_linux_boot_image(grub_addr_t addr, char *args);
1c6ba0
+grub_err_t grub_arch_efi_linux_boot_image(grub_addr_t addr, grub_size_t size,
1c6ba0
+					  char *args, int nx_enabled);
1c6ba0
 #endif
1c6ba0
 
1c6ba0
 grub_addr_t grub_efi_section_addr (const char *section);
1c6ba0
diff --git a/include/grub/efi/linux.h b/include/grub/efi/linux.h
1c6ba0
index 0033d9305a..8130b19590 100644
1c6ba0
--- a/include/grub/efi/linux.h
1c6ba0
+++ b/include/grub/efi/linux.h
1c6ba0
@@ -22,10 +22,23 @@
1c6ba0
 #include <grub/err.h>
1c6ba0
 #include <grub/symbol.h>
1c6ba0
 
1c6ba0
+#define GRUB_MOK_POLICY_NX_REQUIRED   0x1
1c6ba0
+
1c6ba0
 int
1c6ba0
 EXPORT_FUNC(grub_linuxefi_secure_validate) (void *data, grub_uint32_t size);
1c6ba0
+
1c6ba0
 grub_err_t
1c6ba0
-EXPORT_FUNC(grub_efi_linux_boot) (void *kernel_address, grub_off_t offset,
1c6ba0
-				  void *kernel_param);
1c6ba0
+EXPORT_FUNC(grub_efi_linux_boot) (grub_addr_t kernel_address,
1c6ba0
+				  grub_size_t kernel_size,
1c6ba0
+				  grub_off_t handover_offset,
1c6ba0
+				  void *kernel_param, int nx_enabled);
1c6ba0
+
1c6ba0
+grub_err_t
1c6ba0
+EXPORT_FUNC(grub_efi_check_nx_image_support) (grub_addr_t kernel_addr,
1c6ba0
+					      grub_size_t kernel_size,
1c6ba0
+					      int *nx_supported);
1c6ba0
+
1c6ba0
+grub_err_t
1c6ba0
+EXPORT_FUNC(grub_efi_check_nx_required) (int *nx_required);
1c6ba0
 
1c6ba0
 #endif /* ! GRUB_EFI_LINUX_HEADER */
1c6ba0
diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h
1c6ba0
index 2a5e1ee003..a5e623eb04 100644
1c6ba0
--- a/include/grub/efi/pe32.h
1c6ba0
+++ b/include/grub/efi/pe32.h
1c6ba0
@@ -181,6 +181,8 @@ struct grub_pe32_optional_header
1c6ba0
   struct grub_pe32_data_directory reserved_entry;
1c6ba0
 };
1c6ba0
 
1c6ba0
+#define GRUB_PE32_NX_COMPAT 0x0100
1c6ba0
+
1c6ba0
 struct grub_pe64_optional_header
1c6ba0
 {
1c6ba0
   grub_uint16_t magic;