Răsfoiți Sursa

fix parameter-passing corruption on x86_64 for >= 5 args

On x86_64 without HAVE_USE_MS_ABI support, uefi_call_wrapper() is a
variadic function. Parameters >=5 are copied to the stack and, when
passed small immediate values (and possibly other parameters), gcc
would emit a movl instruction before calling uefi_call_wrapper(). As a
result, only the lower 32b of these stack values are significant, the
upper 32b potentially contain garbage. Considering that
uefi_call_wrapper() assumes these arguments are clean 64b values
before calling the efi_callX() trampolines, the latter may be passed
garbage. This makes calling functions like
EFI_PCI_IO_PROTOCOL.Mem.Read()/Write() or BS->OpenProtocol() quite
unreliable.

This patch fixes this by turning uefi_call_wrapper() into a macro that
allows to expose the efi_callX() trampoline signatures to the callers,
so that gcc can know upfront that it has to pass all arguments to
efi_callX() as clean 64b values (eg. movq for immediates). The
_cast64_efi_callX macros are just here to avoid a gcc warning, they do
nothing otherwise.

Signed-off-by: David Decotigny <decot@googlers.com>
Nigel Croxon 12 ani în urmă
părinte
comite
c0b8d974a6
2 a modificat fișierele cu 59 adăugiri și 90 ștergeri
  1. 58 2
      gnu-efi-3.0/inc/x86_64/efibind.h
  2. 1 88
      gnu-efi-3.0/lib/x86_64/callwrap.c

+ 58 - 2
gnu-efi-3.0/inc/x86_64/efibind.h

@@ -290,7 +290,64 @@ typedef uint64_t   UINTN;
 #if defined(HAVE_USE_MS_ABI)
 #define uefi_call_wrapper(func, va_num, ...) func(__VA_ARGS__)
 #else
-UINTN uefi_call_wrapper(void *func, unsigned long va_num, ...);
+/* Prototypes of EFI cdecl -> stdcall trampolines */
+UINT64 efi_call0(void *func);
+UINT64 efi_call1(void *func, UINT64 arg1);
+UINT64 efi_call2(void *func, UINT64 arg1, UINT64 arg2);
+UINT64 efi_call3(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3);
+UINT64 efi_call4(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
+                 UINT64 arg4);
+UINT64 efi_call5(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
+                 UINT64 arg4, UINT64 arg5);
+UINT64 efi_call6(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
+                 UINT64 arg4, UINT64 arg5, UINT64 arg6);
+UINT64 efi_call7(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
+                 UINT64 arg4, UINT64 arg5, UINT64 arg6, UINT64 arg7);
+UINT64 efi_call8(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
+                 UINT64 arg4, UINT64 arg5, UINT64 arg6, UINT64 arg7,
+                 UINT64 arg8);
+UINT64 efi_call9(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
+                 UINT64 arg4, UINT64 arg5, UINT64 arg6, UINT64 arg7,
+                 UINT64 arg8, UINT64 arg9);
+UINT64 efi_call10(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
+                  UINT64 arg4, UINT64 arg5, UINT64 arg6, UINT64 arg7,
+                  UINT64 arg8, UINT64 arg9, UINT64 arg10);
+
+/* Front-ends to efi_callX to avoid compiler warnings */
+#define _cast64_efi_call0(f) \
+  efi_call0(f)
+#define _cast64_efi_call1(f,a1) \
+  efi_call1(f, (UINT64)(a1))
+#define _cast64_efi_call2(f,a1,a2) \
+  efi_call2(f, (UINT64)(a1), (UINT64)(a2))
+#define _cast64_efi_call3(f,a1,a2,a3) \
+  efi_call3(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3))
+#define _cast64_efi_call4(f,a1,a2,a3,a4) \
+  efi_call4(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4))
+#define _cast64_efi_call5(f,a1,a2,a3,a4,a5) \
+  efi_call5(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
+            (UINT64)(a5))
+#define _cast64_efi_call6(f,a1,a2,a3,a4,a5,a6) \
+  efi_call6(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
+            (UINT64)(a5), (UINT64)(a6))
+#define _cast64_efi_call7(f,a1,a2,a3,a4,a5,a6,a7) \
+  efi_call7(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
+            (UINT64)(a5), (UINT64)(a6), (UINT64)(a7))
+#define _cast64_efi_call8(f,a1,a2,a3,a4,a5,a6,a7,a8) \
+  efi_call8(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
+            (UINT64)(a5), (UINT64)(a6), (UINT64)(a7), (UINT64)(a8))
+#define _cast64_efi_call9(f,a1,a2,a3,a4,a5,a6,a7,a8,a9) \
+  efi_call9(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
+            (UINT64)(a5), (UINT64)(a6), (UINT64)(a7), (UINT64)(a8), \
+            (UINT64)(a9))
+#define _cast64_efi_call10(f,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) \
+  efi_call10(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
+             (UINT64)(a5), (UINT64)(a6), (UINT64)(a7), (UINT64)(a8), \
+             (UINT64)(a9), (UINT64)(a10))
+
+#define uefi_call_wrapper(func,va_num,...)                        \
+  _cast64_efi_call ## va_num (func , ##__VA_ARGS__)
+
 #endif
 #define EFI_FUNCTION __attribute__((ms_abi))
 
@@ -299,4 +356,3 @@ UINTN uefi_call_wrapper(void *func, unsigned long va_num, ...);
 #endif
 
 #endif
-

+ 1 - 88
gnu-efi-3.0/lib/x86_64/callwrap.c

@@ -37,91 +37,4 @@
  *  SUCH DAMAGE.
  */
 
-#include "efi.h"
-#include "efistdarg.h"
-
-#if !defined(HAVE_USE_MS_ABI)
-UINT64 efi_call0(void *func);
-UINT64 efi_call1(void *func, UINT64 arg1);
-UINT64 efi_call2(void *func, UINT64 arg1, UINT64 arg2);
-UINT64 efi_call3(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3);
-UINT64 efi_call4(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
-		 UINT64 arg4);
-UINT64 efi_call5(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
-		 UINT64 arg4, UINT64 arg5);
-UINT64 efi_call6(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
-		 UINT64 arg4, UINT64 arg5, UINT64 arg6);
-UINT64 efi_call7(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
-		 UINT64 arg4, UINT64 arg5, UINT64 arg6, UINT64 arg7);
-UINT64 efi_call8(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
-		 UINT64 arg4, UINT64 arg5, UINT64 arg6, UINT64 arg7,
-		 UINT64 arg8);
-UINT64 efi_call9(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
-		 UINT64 arg4, UINT64 arg5, UINT64 arg6, UINT64 arg7,
-		 UINT64 arg8, UINT64 arg9);
-UINT64 efi_call10(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
-		 UINT64 arg4, UINT64 arg5, UINT64 arg6, UINT64 arg7,
-		 UINT64 arg8, UINT64 arg9, UINT64 arg10);
-
-#define EFI_ARG_NUM_MAX 10
-
-EFI_STATUS uefi_call_wrapper(void *fp, unsigned long va_num, ...)
-{
-	va_list ap;
-	int i;
-	unsigned long args[EFI_ARG_NUM_MAX];
-
-	if (va_num > EFI_ARG_NUM_MAX || va_num < 0) {
-		return EFI_LOAD_ERROR;
-	}
-	va_start(ap, va_num);
-	for (i = 0; i < va_num; i++) {
-		args[i] = va_arg(ap, UINT64);
-	}
-	va_end(ap);
-	/* As the number of args grows extend it appropriately */
-	switch (va_num) {
-	case 0:
-		return efi_call0(fp);
-	case 1:
-		return efi_call1(fp, args[0]);
-	case 2:
-		return efi_call2(fp,
-				 args[0], args[1]);
-	case 3:
-		return efi_call3(fp,
-				 args[0], args[1], args[2]);
-	case 4:
-		return efi_call4(fp,
-				 args[0], args[1], args[2], args[3]);
-	case 5:
-		return efi_call5(fp,
-				 args[0], args[1], args[2], args[3],
-				 args[4]);
-	case 6:
-		return efi_call6(fp,
-				 args[0], args[1], args[2], args[3],
-				 args[4], args[5]);
-	case 7:
-		return efi_call7(fp,
-				 args[0], args[1], args[2], args[3],
-				 args[4], args[5], args[6]);
-	case 8:
-		return efi_call8(fp,
-				 args[0], args[1], args[2], args[3],
-				 args[4], args[5], args[6], args[7]);
-	case 9:
-		return efi_call9(fp,
-				 args[0], args[1], args[2], args[3],
-				 args[4], args[5], args[6], args[7],
-				 args[8]);
-	case 10:
-		return efi_call10(fp,
-				  args[0], args[1], args[2], args[3],
-				  args[4], args[5], args[6], args[7],
-				  args[8], args[9]);
-	default:
-		return EFI_LOAD_ERROR;
-	}
-}
-#endif
+/* uefi_call_wrapper() is a macro in efibind.h */