nalika / rpms / grub2

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