public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [PATCH EDK2 v2 0/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
@ 2020-08-13 11:55 wenyi,xie
  2020-08-13 11:55 ` [PATCH EDK2 v2 1/1] " wenyi,xie
  0 siblings, 1 reply; 32+ messages in thread
From: wenyi,xie @ 2020-08-13 11:55 UTC (permalink / raw)
  To: devel, jiewen.yao, jian.j.wang, lersek; +Cc: huangming23, songdongkuang

Main Changes since v1 :
1.add flag IsAuthDataAssigned
2.check IsAuthDataAssigned and result of HashPeImageByType, if either is false, then skip signature verification and step to next loop

cover letter of the v1 : 
https://edk2.groups.io/g/devel/message/64059

Wenyi Xie (1):
  SecurityPkg/DxeImageVerificationLib:Enhanced verification of
    Offset(CVE-2019-14562)

 SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf |   1 +
 SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h   |   1 +
 SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c   | 111 +++++++++++---------
 3 files changed, 63 insertions(+), 50 deletions(-)

-- 
2.20.1.windows.1


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-13 11:55 [PATCH EDK2 v2 0/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset wenyi,xie
@ 2020-08-13 11:55 ` wenyi,xie
  2020-08-13 14:32   ` Yao, Jiewen
  2020-08-13 18:50   ` Laszlo Ersek
  0 siblings, 2 replies; 32+ messages in thread
From: wenyi,xie @ 2020-08-13 11:55 UTC (permalink / raw)
  To: devel, jiewen.yao, jian.j.wang, lersek; +Cc: huangming23, songdongkuang

REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215

There is an integer overflow vulnerability in DxeImageVerificationHandler
function when parsing the PE files attribute certificate table. In cases
where WinCertificate->dwLength is sufficiently large, it's possible to
overflow Offset back to 0 causing an endless loop.

Check offset inbetween VirtualAddress and VirtualAddress + Size.
Using SafeintLib to do offset addition with result check.

Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
---
 SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf |   1 +
 SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h   |   1 +
 SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c   | 111 +++++++++++---------
 3 files changed, 63 insertions(+), 50 deletions(-)

diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
index 1e1a639857e0..a7ac4830b3d4 100644
--- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
+++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
@@ -53,6 +53,7 @@ [LibraryClasses]
   SecurityManagementLib
   PeCoffLib
   TpmMeasurementLib
+  SafeIntLib
 
 [Protocols]
   gEfiFirmwareVolume2ProtocolGuid       ## SOMETIMES_CONSUMES
diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
index 17955ff9774c..060273917d5d 100644
--- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
+++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
@@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 #include <Library/DevicePathLib.h>
 #include <Library/SecurityManagementLib.h>
 #include <Library/PeCoffLib.h>
+#include <Library/SafeIntLib.h>
 #include <Protocol/FirmwareVolume2.h>
 #include <Protocol/DevicePath.h>
 #include <Protocol/BlockIo.h>
diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
index 36b87e16d53d..dbc03e28c05b 100644
--- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
+++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
@@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
   EFI_STATUS                           HashStatus;
   EFI_STATUS                           DbStatus;
   BOOLEAN                              IsFound;
+  UINT32                               AlignedLength;
+  UINT32                               Result;
+  EFI_STATUS                           AddStatus;
+  BOOLEAN                              IsAuthDataAssigned;
 
   SignatureList     = NULL;
   SignatureListSize = 0;
@@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
   Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
   IsVerified        = FALSE;
   IsFound           = FALSE;
+  Result            = 0;
 
   //
   // Check the image type and get policy setting.
@@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
   // The first certificate starts at offset (SecDataDir->VirtualAddress) from the start of the file.
   //
   for (OffSet = SecDataDir->VirtualAddress;
-       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
-       OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) {
+       (OffSet >= SecDataDir->VirtualAddress) && (OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size));) {
+    IsAuthDataAssigned = FALSE;
     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
+    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength);
     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof (WIN_CERTIFICATE) ||
         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate->dwLength) {
       break;
@@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
       }
       AuthData   = PkcsCertData->CertData;
       AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData->Hdr);
+      IsAuthDataAssigned = TRUE;
+      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
     } else if (WinCertificate->wCertificateType == WIN_CERT_TYPE_EFI_GUID) {
       //
       // The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which is described in UEFI Spec.
@@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
       if (WinCertUefiGuid->Hdr.dwLength <= OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
         break;
       }
-      if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {
-        continue;
+      if (CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {
+        AuthData = WinCertUefiGuid->CertData;
+        AuthDataSize = WinCertUefiGuid->Hdr.dwLength - OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
+        IsAuthDataAssigned = TRUE;
+        HashStatus = HashPeImageByType (AuthData, AuthDataSize);
       }
-      AuthData = WinCertUefiGuid->CertData;
-      AuthDataSize = WinCertUefiGuid->Hdr.dwLength - OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
     } else {
       if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
         break;
       }
-      continue;
     }
 
-    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
-    if (EFI_ERROR (HashStatus)) {
-      continue;
-    }
-
-    //
-    // Check the digital signature against the revoked certificate in forbidden database (dbx).
-    //
-    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
-      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
-      IsVerified = FALSE;
-      break;
-    }
-
-    //
-    // Check the digital signature against the valid certificate in allowed database (db).
-    //
-    if (!IsVerified) {
-      if (IsAllowedByDb (AuthData, AuthDataSize)) {
-        IsVerified = TRUE;
+    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
+      //
+      // Check the digital signature against the revoked certificate in forbidden database (dbx).
+      //
+      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
+        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
+        IsVerified = FALSE;
+        break;
       }
-    }
 
-    //
-    // Check the image's hash value.
-    //
-    DbStatus = IsSignatureFoundInDatabase (
-                 EFI_IMAGE_SECURITY_DATABASE1,
-                 mImageDigest,
-                 &mCertType,
-                 mImageDigestSize,
-                 &IsFound
-                 );
-    if (EFI_ERROR (DbStatus) || IsFound) {
-      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
-      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but %s hash of image is found in DBX.\n", mHashTypeStr));
-      IsVerified = FALSE;
-      break;
-    }
+      //
+      // Check the digital signature against the valid certificate in allowed database (db).
+      //
+      if (!IsVerified) {
+        if (IsAllowedByDb (AuthData, AuthDataSize)) {
+          IsVerified = TRUE;
+        }
+      }
 
-    if (!IsVerified) {
+      //
+      // Check the image's hash value.
+      //
       DbStatus = IsSignatureFoundInDatabase (
-                   EFI_IMAGE_SECURITY_DATABASE,
+                   EFI_IMAGE_SECURITY_DATABASE1,
                    mImageDigest,
                    &mCertType,
                    mImageDigestSize,
                    &IsFound
                    );
-      if (!EFI_ERROR (DbStatus) && IsFound) {
-        IsVerified = TRUE;
-      } else {
-        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but signature is not allowed by DB and %s hash of image is not found in DB/DBX.\n", mHashTypeStr));
+      if (EFI_ERROR (DbStatus) || IsFound) {
+        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
+        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but %s hash of image is found in DBX.\n", mHashTypeStr));
+        IsVerified = FALSE;
+        break;
       }
+
+      if (!IsVerified) {
+        DbStatus = IsSignatureFoundInDatabase (
+                     EFI_IMAGE_SECURITY_DATABASE,
+                     mImageDigest,
+                     &mCertType,
+                     mImageDigestSize,
+                     &IsFound
+                     );
+        if (!EFI_ERROR (DbStatus) && IsFound) {
+          IsVerified = TRUE;
+        } else {
+          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but signature is not allowed by DB and %s hash of image is not found in DB/DBX.\n", mHashTypeStr));
+        }
+      }
+    }
+
+    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
+    if (EFI_ERROR (AddStatus)) {
+      break;
     }
+    OffSet = Result;
   }
 
   if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
-- 
2.20.1.windows.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-13 11:55 ` [PATCH EDK2 v2 1/1] " wenyi,xie
@ 2020-08-13 14:32   ` Yao, Jiewen
  2020-08-13 18:50   ` Laszlo Ersek
  1 sibling, 0 replies; 32+ messages in thread
From: Yao, Jiewen @ 2020-08-13 14:32 UTC (permalink / raw)
  To: Wenyi Xie, devel@edk2.groups.io, Wang, Jian J, lersek@redhat.com
  Cc: huangming23@huawei.com, songdongkuang@huawei.com, Yao, Jiewen

Thanks Wenyi.

May I know how you test the new code logic?

Any unit test you can share, such as a mal-formed PE image, which may break the old implementation but is caught by this patch?

Thank you
Yao Jiewen

> -----Original Message-----
> From: Wenyi Xie <xiewenyi2@huawei.com>
> Sent: Thursday, August 13, 2020 7:56 PM
> To: devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>; Wang, Jian J
> <jian.j.wang@intel.com>; lersek@redhat.com
> Cc: huangming23@huawei.com; songdongkuang@huawei.com
> Subject: [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced
> verification of Offset
> 
> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
> 
> There is an integer overflow vulnerability in DxeImageVerificationHandler
> function when parsing the PE files attribute certificate table. In cases
> where WinCertificate->dwLength is sufficiently large, it's possible to
> overflow Offset back to 0 causing an endless loop.
> 
> Check offset inbetween VirtualAddress and VirtualAddress + Size.
> Using SafeintLib to do offset addition with result check.
> 
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Jian J Wang <jian.j.wang@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
> ---
>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf |   1
> +
>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h   |   1 +
>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c   | 111
> +++++++++++---------
>  3 files changed, 63 insertions(+), 50 deletions(-)
> 
> diff --git
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> index 1e1a639857e0..a7ac4830b3d4 100644
> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> @@ -53,6 +53,7 @@ [LibraryClasses]
>    SecurityManagementLib
>    PeCoffLib
>    TpmMeasurementLib
> +  SafeIntLib
> 
>  [Protocols]
>    gEfiFirmwareVolume2ProtocolGuid       ## SOMETIMES_CONSUMES
> diff --git
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> index 17955ff9774c..060273917d5d 100644
> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
>  #include <Library/DevicePathLib.h>
>  #include <Library/SecurityManagementLib.h>
>  #include <Library/PeCoffLib.h>
> +#include <Library/SafeIntLib.h>
>  #include <Protocol/FirmwareVolume2.h>
>  #include <Protocol/DevicePath.h>
>  #include <Protocol/BlockIo.h>
> diff --git
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> index 36b87e16d53d..dbc03e28c05b 100644
> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
>    EFI_STATUS                           HashStatus;
>    EFI_STATUS                           DbStatus;
>    BOOLEAN                              IsFound;
> +  UINT32                               AlignedLength;
> +  UINT32                               Result;
> +  EFI_STATUS                           AddStatus;
> +  BOOLEAN                              IsAuthDataAssigned;
> 
>    SignatureList     = NULL;
>    SignatureListSize = 0;
> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
>    IsVerified        = FALSE;
>    IsFound           = FALSE;
> +  Result            = 0;
> 
>    //
>    // Check the image type and get policy setting.
> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
>    // The first certificate starts at offset (SecDataDir->VirtualAddress) from the
> start of the file.
>    //
>    for (OffSet = SecDataDir->VirtualAddress;
> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >dwLength))) {
> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet < (SecDataDir-
> >VirtualAddress + SecDataDir->Size));) {
> +    IsAuthDataAssigned = FALSE;
>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >dwLength);
>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
> (WIN_CERTIFICATE) ||
>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
> >dwLength) {
>        break;
> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
>        }
>        AuthData   = PkcsCertData->CertData;
>        AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData->Hdr);
> +      IsAuthDataAssigned = TRUE;
> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>      } else if (WinCertificate->wCertificateType == WIN_CERT_TYPE_EFI_GUID) {
>        //
>        // The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which is
> described in UEFI Spec.
> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
>        if (WinCertUefiGuid->Hdr.dwLength <=
> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
>          break;
>        }
> -      if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {
> -        continue;
> +      if (CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {
> +        AuthData = WinCertUefiGuid->CertData;
> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
> +        IsAuthDataAssigned = TRUE;
> +        HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>        }
> -      AuthData = WinCertUefiGuid->CertData;
> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>      } else {
>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
>          break;
>        }
> -      continue;
>      }
> 
> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> -    if (EFI_ERROR (HashStatus)) {
> -      continue;
> -    }
> -
> -    //
> -    // Check the digital signature against the revoked certificate in forbidden
> database (dbx).
> -    //
> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> -      IsVerified = FALSE;
> -      break;
> -    }
> -
> -    //
> -    // Check the digital signature against the valid certificate in allowed database
> (db).
> -    //
> -    if (!IsVerified) {
> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
> -        IsVerified = TRUE;
> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
> +      //
> +      // Check the digital signature against the revoked certificate in forbidden
> database (dbx).
> +      //
> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> +        IsVerified = FALSE;
> +        break;
>        }
> -    }
> 
> -    //
> -    // Check the image's hash value.
> -    //
> -    DbStatus = IsSignatureFoundInDatabase (
> -                 EFI_IMAGE_SECURITY_DATABASE1,
> -                 mImageDigest,
> -                 &mCertType,
> -                 mImageDigestSize,
> -                 &IsFound
> -                 );
> -    if (EFI_ERROR (DbStatus) || IsFound) {
> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but %s
> hash of image is found in DBX.\n", mHashTypeStr));
> -      IsVerified = FALSE;
> -      break;
> -    }
> +      //
> +      // Check the digital signature against the valid certificate in allowed
> database (db).
> +      //
> +      if (!IsVerified) {
> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
> +          IsVerified = TRUE;
> +        }
> +      }
> 
> -    if (!IsVerified) {
> +      //
> +      // Check the image's hash value.
> +      //
>        DbStatus = IsSignatureFoundInDatabase (
> -                   EFI_IMAGE_SECURITY_DATABASE,
> +                   EFI_IMAGE_SECURITY_DATABASE1,
>                     mImageDigest,
>                     &mCertType,
>                     mImageDigestSize,
>                     &IsFound
>                     );
> -      if (!EFI_ERROR (DbStatus) && IsFound) {
> -        IsVerified = TRUE;
> -      } else {
> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but
> signature is not allowed by DB and %s hash of image is not found in DB/DBX.\n",
> mHashTypeStr));
> +      if (EFI_ERROR (DbStatus) || IsFound) {
> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but %s
> hash of image is found in DBX.\n", mHashTypeStr));
> +        IsVerified = FALSE;
> +        break;
>        }
> +
> +      if (!IsVerified) {
> +        DbStatus = IsSignatureFoundInDatabase (
> +                     EFI_IMAGE_SECURITY_DATABASE,
> +                     mImageDigest,
> +                     &mCertType,
> +                     mImageDigestSize,
> +                     &IsFound
> +                     );
> +        if (!EFI_ERROR (DbStatus) && IsFound) {
> +          IsVerified = TRUE;
> +        } else {
> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but
> signature is not allowed by DB and %s hash of image is not found in DB/DBX.\n",
> mHashTypeStr));
> +        }
> +      }
> +    }
> +
> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
> +    if (EFI_ERROR (AddStatus)) {
> +      break;
>      }
> +    OffSet = Result;
>    }
> 
>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
> --
> 2.20.1.windows.1


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-13 11:55 ` [PATCH EDK2 v2 1/1] " wenyi,xie
  2020-08-13 14:32   ` Yao, Jiewen
@ 2020-08-13 18:50   ` Laszlo Ersek
  2020-08-14  7:53     ` wenyi,xie
  1 sibling, 1 reply; 32+ messages in thread
From: Laszlo Ersek @ 2020-08-13 18:50 UTC (permalink / raw)
  To: Wenyi Xie, devel, jiewen.yao, jian.j.wang; +Cc: huangming23, songdongkuang

On 08/13/20 13:55, Wenyi Xie wrote:
> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
>
> There is an integer overflow vulnerability in DxeImageVerificationHandler
> function when parsing the PE files attribute certificate table. In cases
> where WinCertificate->dwLength is sufficiently large, it's possible to
> overflow Offset back to 0 causing an endless loop.
>
> Check offset inbetween VirtualAddress and VirtualAddress + Size.
> Using SafeintLib to do offset addition with result check.
>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Jian J Wang <jian.j.wang@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
> ---
>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf |   1 +
>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h   |   1 +
>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c   | 111 +++++++++++---------
>  3 files changed, 63 insertions(+), 50 deletions(-)
>
> diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> index 1e1a639857e0..a7ac4830b3d4 100644
> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> @@ -53,6 +53,7 @@ [LibraryClasses]
>    SecurityManagementLib
>    PeCoffLib
>    TpmMeasurementLib
> +  SafeIntLib
>
>  [Protocols]
>    gEfiFirmwareVolume2ProtocolGuid       ## SOMETIMES_CONSUMES
> diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> index 17955ff9774c..060273917d5d 100644
> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
>  #include <Library/DevicePathLib.h>
>  #include <Library/SecurityManagementLib.h>
>  #include <Library/PeCoffLib.h>
> +#include <Library/SafeIntLib.h>
>  #include <Protocol/FirmwareVolume2.h>
>  #include <Protocol/DevicePath.h>
>  #include <Protocol/BlockIo.h>
> diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> index 36b87e16d53d..dbc03e28c05b 100644
> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
>    EFI_STATUS                           HashStatus;
>    EFI_STATUS                           DbStatus;
>    BOOLEAN                              IsFound;
> +  UINT32                               AlignedLength;
> +  UINT32                               Result;
> +  EFI_STATUS                           AddStatus;
> +  BOOLEAN                              IsAuthDataAssigned;
>
>    SignatureList     = NULL;
>    SignatureListSize = 0;
> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
>    IsVerified        = FALSE;
>    IsFound           = FALSE;
> +  Result            = 0;
>
>    //
>    // Check the image type and get policy setting.
> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
>    // The first certificate starts at offset (SecDataDir->VirtualAddress) from the start of the file.
>    //
>    for (OffSet = SecDataDir->VirtualAddress;
> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) {
> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size));) {
> +    IsAuthDataAssigned = FALSE;
>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength);

I disagree with this patch.

The primary reason for my disagreement is that the bug report
<https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is inexact, and
so this patch tries to fix the wrong thing.

With edk2 master at commit 65904cdbb33c, it is *not* possible to
overflow the OffSet variable to zero with "WinCertificate->dwLength"
*purely*, and cause an endless loop. Note that we have (at commit
65904cdbb33c):

  for (OffSet = SecDataDir->VirtualAddress;
       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
       OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) {
    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof (WIN_CERTIFICATE) ||
        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate->dwLength) {
      break;
    }

The last sub-condition checks whether the Security Data Directory has
enough room left for "WinCertificate->dwLength". If not, then we break
out of the loop.

If we *do* have enough room, that is:

  (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >= WinCertificate->dwLength

then we have (by adding OffSet to both sides):

  SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet + WinCertificate->dwLength

The left hand side is a known-good UINT32, and so incrementing OffSet (a
UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32) does not
cause an overflow.

Instead, the problem is with the alignment. The "if" statement checks
whether we have enough room for "dwLength", but then "OffSet" is
advanced by "dwLength" *aligned up* to the next multiple of 8. And that
may indeed cause various overflows.

Now, the main problem with the present patch is that it does not fix one
of those overflows. Namely, consider that "dwLength" is very close to
MAX_UINT32 (or even think it's exactly MAX_UINT32). Then aligning it up
to the next multiple of 8 will yield 0. In other words, "AlignedLength"
will be zero.

And when that happens, there's going to be an infinite loop just the
same: "OffSet" will not be zero, but it will be *stuck*. The
SafeUint32Add() call at the bottom will succeed, but it will not change
the value of "OffSet".

More at the bottom.


>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof (WIN_CERTIFICATE) ||
>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate->dwLength) {
>        break;
> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
>        }
>        AuthData   = PkcsCertData->CertData;
>        AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData->Hdr);
> +      IsAuthDataAssigned = TRUE;
> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>      } else if (WinCertificate->wCertificateType == WIN_CERT_TYPE_EFI_GUID) {
>        //
>        // The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which is described in UEFI Spec.
> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
>        if (WinCertUefiGuid->Hdr.dwLength <= OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
>          break;
>        }
> -      if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {
> -        continue;
> +      if (CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {
> +        AuthData = WinCertUefiGuid->CertData;
> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength - OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
> +        IsAuthDataAssigned = TRUE;
> +        HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>        }
> -      AuthData = WinCertUefiGuid->CertData;
> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength - OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>      } else {
>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
>          break;
>        }
> -      continue;
>      }
>
> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> -    if (EFI_ERROR (HashStatus)) {
> -      continue;
> -    }
> -
> -    //
> -    // Check the digital signature against the revoked certificate in forbidden database (dbx).
> -    //
> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> -      IsVerified = FALSE;
> -      break;
> -    }
> -
> -    //
> -    // Check the digital signature against the valid certificate in allowed database (db).
> -    //
> -    if (!IsVerified) {
> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
> -        IsVerified = TRUE;
> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
> +      //
> +      // Check the digital signature against the revoked certificate in forbidden database (dbx).
> +      //
> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> +        IsVerified = FALSE;
> +        break;
>        }
> -    }
>
> -    //
> -    // Check the image's hash value.
> -    //
> -    DbStatus = IsSignatureFoundInDatabase (
> -                 EFI_IMAGE_SECURITY_DATABASE1,
> -                 mImageDigest,
> -                 &mCertType,
> -                 mImageDigestSize,
> -                 &IsFound
> -                 );
> -    if (EFI_ERROR (DbStatus) || IsFound) {
> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but %s hash of image is found in DBX.\n", mHashTypeStr));
> -      IsVerified = FALSE;
> -      break;
> -    }
> +      //
> +      // Check the digital signature against the valid certificate in allowed database (db).
> +      //
> +      if (!IsVerified) {
> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
> +          IsVerified = TRUE;
> +        }
> +      }
>
> -    if (!IsVerified) {
> +      //
> +      // Check the image's hash value.
> +      //
>        DbStatus = IsSignatureFoundInDatabase (
> -                   EFI_IMAGE_SECURITY_DATABASE,
> +                   EFI_IMAGE_SECURITY_DATABASE1,
>                     mImageDigest,
>                     &mCertType,
>                     mImageDigestSize,
>                     &IsFound
>                     );
> -      if (!EFI_ERROR (DbStatus) && IsFound) {
> -        IsVerified = TRUE;
> -      } else {
> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but signature is not allowed by DB and %s hash of image is not found in DB/DBX.\n", mHashTypeStr));
> +      if (EFI_ERROR (DbStatus) || IsFound) {
> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but %s hash of image is found in DBX.\n", mHashTypeStr));
> +        IsVerified = FALSE;
> +        break;
>        }
> +
> +      if (!IsVerified) {
> +        DbStatus = IsSignatureFoundInDatabase (
> +                     EFI_IMAGE_SECURITY_DATABASE,
> +                     mImageDigest,
> +                     &mCertType,
> +                     mImageDigestSize,
> +                     &IsFound
> +                     );
> +        if (!EFI_ERROR (DbStatus) && IsFound) {
> +          IsVerified = TRUE;
> +        } else {
> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but signature is not allowed by DB and %s hash of image is not found in DB/DBX.\n", mHashTypeStr));
> +        }
> +      }
> +    }
> +
> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
> +    if (EFI_ERROR (AddStatus)) {
> +      break;
>      }
> +    OffSet = Result;
>    }
>
>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
>

There are other (smaller) reasons why I dislike this patch:

- The "IsAuthDataAssigned" variable is superfluous; we could use the
existent "AuthData" variable (with a NULL-check and a NULL-assignment)
similarly.

- The patch complicates / reorganizes the control flow needlessly. This
complication originates from placing the checked "OffSet" increment at
the bottom of the loop, which then requires the removal of all the
"continue" statements. But we don't need to check-and-increment at the
bottom. We can keep the increment inside the "for" statement, only
extend the *existent* room check (which I've quoted) to take the
alignment into account as well. If there is enough room for the
alignment in the security data directory, then that guarantees there
won't be a UINT32 overflow either.

All in all, I'm proposing the following three patches instead. The first
two patches are preparation, the last patch is the fix.

Patch#1:

> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep 17 00:00:00 2001
> From: Laszlo Ersek <lersek@redhat.com>
> Date: Thu, 13 Aug 2020 19:11:39 +0200
> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib: extract
>  SecDataDirEnd, SecDataDirLeft
>
> The following two quantities:
>
>   SecDataDir->VirtualAddress + SecDataDir->Size
>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
>
> are used multiple times in DxeImageVerificationHandler(). Introduce helper
> variables for them: "SecDataDirEnd" and "SecDataDirLeft", respectively.
> This saves us multiple calculations and significantly simplifies the code.
>
> Note that all three summands above have type UINT32, therefore the new
> variables are also of type UINT32.
>
> This patch does not change behavior.
>
> (Note that the code already handles the case when the
>
>   SecDataDir->VirtualAddress + SecDataDir->Size
>
> UINT32 addition overflows -- namely, in that case, the certificate loop is
> never entered, and the corruption check right after the loop fires.)
>
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> ---
>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c | 12 ++++++++----
>  1 file changed, 8 insertions(+), 4 deletions(-)
>
> diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> index 36b87e16d53d..8761980c88aa 100644
> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
>    UINT8                                *AuthData;
>    UINTN                                AuthDataSize;
>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
> +  UINT32                               SecDataDirEnd;
> +  UINT32                               SecDataDirLeft;
>    UINT32                               OffSet;
>    CHAR16                               *NameStr;
>    RETURN_STATUS                        PeCoffStatus;
> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
>    // "Attribute Certificate Table".
>    // The first certificate starts at offset (SecDataDir->VirtualAddress) from the start of the file.
>    //
> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
>    for (OffSet = SecDataDir->VirtualAddress;
> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> +       OffSet < SecDataDirEnd;
>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) {
>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof (WIN_CERTIFICATE) ||
> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate->dwLength) {
> +    SecDataDirLeft = SecDataDirEnd - OffSet;
> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
> +        SecDataDirLeft < WinCertificate->dwLength) {
>        break;
>      }
>
> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
>      }
>    }
>
> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
> +  if (OffSet != SecDataDirEnd) {
>      //
>      // The Size in Certificate Table or the attribute certificate table is corrupted.
>      //
> --
> 2.19.1.3.g30247aa5d201
>

Patch#2:

> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep 17 00:00:00 2001
> From: Laszlo Ersek <lersek@redhat.com>
> Date: Thu, 13 Aug 2020 19:19:06 +0200
> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib: assign
>  WinCertificate after size check
>
> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) check only
> guards the de-referencing of the "WinCertificate" pointer. It does not
> guard the calculation of hte pointer itself:
>
>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>
> This is wrong; if we don't know for sure that we have enough room for a
> WIN_CERTIFICATE, then even creating such a pointer, not just
> de-referencing it, may invoke undefined behavior.
>
> Move the pointer calculation after the size check.
>
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> ---
>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c | 8 +++++---
>  1 file changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> index 8761980c88aa..461ed7cfb5ac 100644
> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
>    for (OffSet = SecDataDir->VirtualAddress;
>         OffSet < SecDataDirEnd;
>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) {
> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>      SecDataDirLeft = SecDataDirEnd - OffSet;
> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
> -        SecDataDirLeft < WinCertificate->dwLength) {
> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
> +      break;
> +    }
> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> +    if (SecDataDirLeft < WinCertificate->dwLength) {
>        break;
>      }
>
> --
> 2.19.1.3.g30247aa5d201
>

Patch#3:

> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17 00:00:00 2001
> From: Laszlo Ersek <lersek@redhat.com>
> Date: Thu, 13 Aug 2020 19:34:33 +0200
> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch alignment
>  overflow (CVE-2019-14562)
>
> The DxeImageVerificationHandler() function currently checks whether
> "SecDataDir" has enough room for "WinCertificate->dwLength". However, for
> advancing "OffSet", "WinCertificate->dwLength" is aligned to the next
> multiple of 8. If "WinCertificate->dwLength" is large enough, the
> alignment will return 0, and "OffSet" will be stuck at the same value.
>
> Check whether "SecDataDir" has room left for both
> "WinCertificate->dwLength" and the alignment.
>
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> ---
>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> index 461ed7cfb5ac..e38eb981b7a0 100644
> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
>        break;
>      }
>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> -    if (SecDataDirLeft < WinCertificate->dwLength) {
> +    if (SecDataDirLeft < WinCertificate->dwLength ||
> +        (SecDataDirLeft - WinCertificate->dwLength <
> +         ALIGN_SIZE (WinCertificate->dwLength))) {
>        break;
>      }
>
> --
> 2.19.1.3.g30247aa5d201
>

If Wenyi and the reviewers are OK with these patches, I can submit them
as a standalone patch series.

Note that I do not have any reproducer for the issue; the best testing
that I could offer would be some light-weight Secure Boot regression
tests.

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-13 18:50   ` Laszlo Ersek
@ 2020-08-14  7:53     ` wenyi,xie
  2020-08-14  8:53       ` [edk2-devel] " Yao, Jiewen
  0 siblings, 1 reply; 32+ messages in thread
From: wenyi,xie @ 2020-08-14  7:53 UTC (permalink / raw)
  To: Laszlo Ersek, devel, jiewen.yao, jian.j.wang; +Cc: huangming23, songdongkuang

To Laszlo,
Thank you for your detailed description, I agree with what you analyzed and I'm OK with your patches, it's
correct and much simpler.

To Jiewen,
Sorry, I don't have environment to reproduce the issue.

Thanks
Wenyi

On 2020/8/14 2:50, Laszlo Ersek wrote:
> On 08/13/20 13:55, Wenyi Xie wrote:
>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
>>
>> There is an integer overflow vulnerability in DxeImageVerificationHandler
>> function when parsing the PE files attribute certificate table. In cases
>> where WinCertificate->dwLength is sufficiently large, it's possible to
>> overflow Offset back to 0 causing an endless loop.
>>
>> Check offset inbetween VirtualAddress and VirtualAddress + Size.
>> Using SafeintLib to do offset addition with result check.
>>
>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>> Cc: Jian J Wang <jian.j.wang@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
>> ---
>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf |   1 +
>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h   |   1 +
>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c   | 111 +++++++++++---------
>>  3 files changed, 63 insertions(+), 50 deletions(-)
>>
>> diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
>> index 1e1a639857e0..a7ac4830b3d4 100644
>> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
>> +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
>> @@ -53,6 +53,7 @@ [LibraryClasses]
>>    SecurityManagementLib
>>    PeCoffLib
>>    TpmMeasurementLib
>> +  SafeIntLib
>>
>>  [Protocols]
>>    gEfiFirmwareVolume2ProtocolGuid       ## SOMETIMES_CONSUMES
>> diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
>> index 17955ff9774c..060273917d5d 100644
>> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
>> +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
>>  #include <Library/DevicePathLib.h>
>>  #include <Library/SecurityManagementLib.h>
>>  #include <Library/PeCoffLib.h>
>> +#include <Library/SafeIntLib.h>
>>  #include <Protocol/FirmwareVolume2.h>
>>  #include <Protocol/DevicePath.h>
>>  #include <Protocol/BlockIo.h>
>> diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> index 36b87e16d53d..dbc03e28c05b 100644
>> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
>>    EFI_STATUS                           HashStatus;
>>    EFI_STATUS                           DbStatus;
>>    BOOLEAN                              IsFound;
>> +  UINT32                               AlignedLength;
>> +  UINT32                               Result;
>> +  EFI_STATUS                           AddStatus;
>> +  BOOLEAN                              IsAuthDataAssigned;
>>
>>    SignatureList     = NULL;
>>    SignatureListSize = 0;
>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
>>    IsVerified        = FALSE;
>>    IsFound           = FALSE;
>> +  Result            = 0;
>>
>>    //
>>    // Check the image type and get policy setting.
>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
>>    // The first certificate starts at offset (SecDataDir->VirtualAddress) from the start of the file.
>>    //
>>    for (OffSet = SecDataDir->VirtualAddress;
>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) {
>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size));) {
>> +    IsAuthDataAssigned = FALSE;
>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength);
> 
> I disagree with this patch.
> 
> The primary reason for my disagreement is that the bug report
> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is inexact, and
> so this patch tries to fix the wrong thing.
> 
> With edk2 master at commit 65904cdbb33c, it is *not* possible to
> overflow the OffSet variable to zero with "WinCertificate->dwLength"
> *purely*, and cause an endless loop. Note that we have (at commit
> 65904cdbb33c):
> 
>   for (OffSet = SecDataDir->VirtualAddress;
>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) {
>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof (WIN_CERTIFICATE) ||
>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate->dwLength) {
>       break;
>     }
> 
> The last sub-condition checks whether the Security Data Directory has
> enough room left for "WinCertificate->dwLength". If not, then we break
> out of the loop.
> 
> If we *do* have enough room, that is:
> 
>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >= WinCertificate->dwLength
> 
> then we have (by adding OffSet to both sides):
> 
>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet + WinCertificate->dwLength
> 
> The left hand side is a known-good UINT32, and so incrementing OffSet (a
> UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32) does not
> cause an overflow.
> 
> Instead, the problem is with the alignment. The "if" statement checks
> whether we have enough room for "dwLength", but then "OffSet" is
> advanced by "dwLength" *aligned up* to the next multiple of 8. And that
> may indeed cause various overflows.
> 
> Now, the main problem with the present patch is that it does not fix one
> of those overflows. Namely, consider that "dwLength" is very close to
> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then aligning it up
> to the next multiple of 8 will yield 0. In other words, "AlignedLength"
> will be zero.
> 
> And when that happens, there's going to be an infinite loop just the
> same: "OffSet" will not be zero, but it will be *stuck*. The
> SafeUint32Add() call at the bottom will succeed, but it will not change
> the value of "OffSet".
> 
> More at the bottom.
> 
> 
>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof (WIN_CERTIFICATE) ||
>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate->dwLength) {
>>        break;
>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
>>        }
>>        AuthData   = PkcsCertData->CertData;
>>        AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData->Hdr);
>> +      IsAuthDataAssigned = TRUE;
>> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>      } else if (WinCertificate->wCertificateType == WIN_CERT_TYPE_EFI_GUID) {
>>        //
>>        // The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which is described in UEFI Spec.
>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
>>        if (WinCertUefiGuid->Hdr.dwLength <= OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
>>          break;
>>        }
>> -      if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {
>> -        continue;
>> +      if (CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {
>> +        AuthData = WinCertUefiGuid->CertData;
>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength - OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>> +        IsAuthDataAssigned = TRUE;
>> +        HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>        }
>> -      AuthData = WinCertUefiGuid->CertData;
>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength - OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>      } else {
>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
>>          break;
>>        }
>> -      continue;
>>      }
>>
>> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>> -    if (EFI_ERROR (HashStatus)) {
>> -      continue;
>> -    }
>> -
>> -    //
>> -    // Check the digital signature against the revoked certificate in forbidden database (dbx).
>> -    //
>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>> -      IsVerified = FALSE;
>> -      break;
>> -    }
>> -
>> -    //
>> -    // Check the digital signature against the valid certificate in allowed database (db).
>> -    //
>> -    if (!IsVerified) {
>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
>> -        IsVerified = TRUE;
>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
>> +      //
>> +      // Check the digital signature against the revoked certificate in forbidden database (dbx).
>> +      //
>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>> +        IsVerified = FALSE;
>> +        break;
>>        }
>> -    }
>>
>> -    //
>> -    // Check the image's hash value.
>> -    //
>> -    DbStatus = IsSignatureFoundInDatabase (
>> -                 EFI_IMAGE_SECURITY_DATABASE1,
>> -                 mImageDigest,
>> -                 &mCertType,
>> -                 mImageDigestSize,
>> -                 &IsFound
>> -                 );
>> -    if (EFI_ERROR (DbStatus) || IsFound) {
>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but %s hash of image is found in DBX.\n", mHashTypeStr));
>> -      IsVerified = FALSE;
>> -      break;
>> -    }
>> +      //
>> +      // Check the digital signature against the valid certificate in allowed database (db).
>> +      //
>> +      if (!IsVerified) {
>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
>> +          IsVerified = TRUE;
>> +        }
>> +      }
>>
>> -    if (!IsVerified) {
>> +      //
>> +      // Check the image's hash value.
>> +      //
>>        DbStatus = IsSignatureFoundInDatabase (
>> -                   EFI_IMAGE_SECURITY_DATABASE,
>> +                   EFI_IMAGE_SECURITY_DATABASE1,
>>                     mImageDigest,
>>                     &mCertType,
>>                     mImageDigestSize,
>>                     &IsFound
>>                     );
>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
>> -        IsVerified = TRUE;
>> -      } else {
>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but signature is not allowed by DB and %s hash of image is not found in DB/DBX.\n", mHashTypeStr));
>> +      if (EFI_ERROR (DbStatus) || IsFound) {
>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but %s hash of image is found in DBX.\n", mHashTypeStr));
>> +        IsVerified = FALSE;
>> +        break;
>>        }
>> +
>> +      if (!IsVerified) {
>> +        DbStatus = IsSignatureFoundInDatabase (
>> +                     EFI_IMAGE_SECURITY_DATABASE,
>> +                     mImageDigest,
>> +                     &mCertType,
>> +                     mImageDigestSize,
>> +                     &IsFound
>> +                     );
>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
>> +          IsVerified = TRUE;
>> +        } else {
>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but signature is not allowed by DB and %s hash of image is not found in DB/DBX.\n", mHashTypeStr));
>> +        }
>> +      }
>> +    }
>> +
>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
>> +    if (EFI_ERROR (AddStatus)) {
>> +      break;
>>      }
>> +    OffSet = Result;
>>    }
>>
>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
>>
> 
> There are other (smaller) reasons why I dislike this patch:
> 
> - The "IsAuthDataAssigned" variable is superfluous; we could use the
> existent "AuthData" variable (with a NULL-check and a NULL-assignment)
> similarly.
> 
> - The patch complicates / reorganizes the control flow needlessly. This
> complication originates from placing the checked "OffSet" increment at
> the bottom of the loop, which then requires the removal of all the
> "continue" statements. But we don't need to check-and-increment at the
> bottom. We can keep the increment inside the "for" statement, only
> extend the *existent* room check (which I've quoted) to take the
> alignment into account as well. If there is enough room for the
> alignment in the security data directory, then that guarantees there
> won't be a UINT32 overflow either.
> 
> All in all, I'm proposing the following three patches instead. The first
> two patches are preparation, the last patch is the fix.
> 
> Patch#1:
> 
>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep 17 00:00:00 2001
>> From: Laszlo Ersek <lersek@redhat.com>
>> Date: Thu, 13 Aug 2020 19:11:39 +0200
>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib: extract
>>  SecDataDirEnd, SecDataDirLeft
>>
>> The following two quantities:
>>
>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
>>
>> are used multiple times in DxeImageVerificationHandler(). Introduce helper
>> variables for them: "SecDataDirEnd" and "SecDataDirLeft", respectively.
>> This saves us multiple calculations and significantly simplifies the code.
>>
>> Note that all three summands above have type UINT32, therefore the new
>> variables are also of type UINT32.
>>
>> This patch does not change behavior.
>>
>> (Note that the code already handles the case when the
>>
>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>
>> UINT32 addition overflows -- namely, in that case, the certificate loop is
>> never entered, and the corruption check right after the loop fires.)
>>
>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>> ---
>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c | 12 ++++++++----
>>  1 file changed, 8 insertions(+), 4 deletions(-)
>>
>> diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> index 36b87e16d53d..8761980c88aa 100644
>> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
>>    UINT8                                *AuthData;
>>    UINTN                                AuthDataSize;
>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
>> +  UINT32                               SecDataDirEnd;
>> +  UINT32                               SecDataDirLeft;
>>    UINT32                               OffSet;
>>    CHAR16                               *NameStr;
>>    RETURN_STATUS                        PeCoffStatus;
>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
>>    // "Attribute Certificate Table".
>>    // The first certificate starts at offset (SecDataDir->VirtualAddress) from the start of the file.
>>    //
>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
>>    for (OffSet = SecDataDir->VirtualAddress;
>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>> +       OffSet < SecDataDirEnd;
>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) {
>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof (WIN_CERTIFICATE) ||
>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate->dwLength) {
>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>> +        SecDataDirLeft < WinCertificate->dwLength) {
>>        break;
>>      }
>>
>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
>>      }
>>    }
>>
>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
>> +  if (OffSet != SecDataDirEnd) {
>>      //
>>      // The Size in Certificate Table or the attribute certificate table is corrupted.
>>      //
>> --
>> 2.19.1.3.g30247aa5d201
>>
> 
> Patch#2:
> 
>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep 17 00:00:00 2001
>> From: Laszlo Ersek <lersek@redhat.com>
>> Date: Thu, 13 Aug 2020 19:19:06 +0200
>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib: assign
>>  WinCertificate after size check
>>
>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) check only
>> guards the de-referencing of the "WinCertificate" pointer. It does not
>> guard the calculation of hte pointer itself:
>>
>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>
>> This is wrong; if we don't know for sure that we have enough room for a
>> WIN_CERTIFICATE, then even creating such a pointer, not just
>> de-referencing it, may invoke undefined behavior.
>>
>> Move the pointer calculation after the size check.
>>
>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>> ---
>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c | 8 +++++---
>>  1 file changed, 5 insertions(+), 3 deletions(-)
>>
>> diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> index 8761980c88aa..461ed7cfb5ac 100644
>> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
>>    for (OffSet = SecDataDir->VirtualAddress;
>>         OffSet < SecDataDirEnd;
>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) {
>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>      SecDataDirLeft = SecDataDirEnd - OffSet;
>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>> -        SecDataDirLeft < WinCertificate->dwLength) {
>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
>> +      break;
>> +    }
>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
>>        break;
>>      }
>>
>> --
>> 2.19.1.3.g30247aa5d201
>>
> 
> Patch#3:
> 
>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17 00:00:00 2001
>> From: Laszlo Ersek <lersek@redhat.com>
>> Date: Thu, 13 Aug 2020 19:34:33 +0200
>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch alignment
>>  overflow (CVE-2019-14562)
>>
>> The DxeImageVerificationHandler() function currently checks whether
>> "SecDataDir" has enough room for "WinCertificate->dwLength". However, for
>> advancing "OffSet", "WinCertificate->dwLength" is aligned to the next
>> multiple of 8. If "WinCertificate->dwLength" is large enough, the
>> alignment will return 0, and "OffSet" will be stuck at the same value.
>>
>> Check whether "SecDataDir" has room left for both
>> "WinCertificate->dwLength" and the alignment.
>>
>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>> ---
>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c | 4 +++-
>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>
>> diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> index 461ed7cfb5ac..e38eb981b7a0 100644
>> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
>>        break;
>>      }
>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
>> +        (SecDataDirLeft - WinCertificate->dwLength <
>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
>>        break;
>>      }
>>
>> --
>> 2.19.1.3.g30247aa5d201
>>
> 
> If Wenyi and the reviewers are OK with these patches, I can submit them
> as a standalone patch series.
> 
> Note that I do not have any reproducer for the issue; the best testing
> that I could offer would be some light-weight Secure Boot regression
> tests.
> 
> Thanks
> Laszlo
> 
> 
> .
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-14  7:53     ` wenyi,xie
@ 2020-08-14  8:53       ` Yao, Jiewen
  2020-08-14 11:29         ` wenyi,xie
  2020-08-17 16:52         ` Laszlo Ersek
  0 siblings, 2 replies; 32+ messages in thread
From: Yao, Jiewen @ 2020-08-14  8:53 UTC (permalink / raw)
  To: devel@edk2.groups.io, xiewenyi2@huawei.com, Laszlo Ersek,
	Wang, Jian J
  Cc: huangming23@huawei.com, songdongkuang@huawei.com

> To Jiewen,
> Sorry, I don't have environment to reproduce the issue.

Please help me understand, if you don’t have environment to reproduce the issue, how do you guarantee that your patch does fix the problem and we don’t have any other vulnerabilities?

Thank you
Yao Jiewen


> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of wenyi,xie
> via groups.io
> Sent: Friday, August 14, 2020 3:54 PM
> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io; Yao, Jiewen
> <jiewen.yao@intel.com>; Wang, Jian J <jian.j.wang@intel.com>
> Cc: huangming23@huawei.com; songdongkuang@huawei.com
> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> 
> To Laszlo,
> Thank you for your detailed description, I agree with what you analyzed and I'm
> OK with your patches, it's
> correct and much simpler.
> 
> To Jiewen,
> Sorry, I don't have environment to reproduce the issue.
> 
> Thanks
> Wenyi
> 
> On 2020/8/14 2:50, Laszlo Ersek wrote:
> > On 08/13/20 13:55, Wenyi Xie wrote:
> >> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
> >>
> >> There is an integer overflow vulnerability in DxeImageVerificationHandler
> >> function when parsing the PE files attribute certificate table. In cases
> >> where WinCertificate->dwLength is sufficiently large, it's possible to
> >> overflow Offset back to 0 causing an endless loop.
> >>
> >> Check offset inbetween VirtualAddress and VirtualAddress + Size.
> >> Using SafeintLib to do offset addition with result check.
> >>
> >> Cc: Jiewen Yao <jiewen.yao@intel.com>
> >> Cc: Jian J Wang <jian.j.wang@intel.com>
> >> Cc: Laszlo Ersek <lersek@redhat.com>
> >> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
> >> ---
> >>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf |
> 1 +
> >>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h   |
> 1 +
> >>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c   |
> 111 +++++++++++---------
> >>  3 files changed, 63 insertions(+), 50 deletions(-)
> >>
> >> diff --git
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> >> index 1e1a639857e0..a7ac4830b3d4 100644
> >> ---
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> >> +++
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> >> @@ -53,6 +53,7 @@ [LibraryClasses]
> >>    SecurityManagementLib
> >>    PeCoffLib
> >>    TpmMeasurementLib
> >> +  SafeIntLib
> >>
> >>  [Protocols]
> >>    gEfiFirmwareVolume2ProtocolGuid       ## SOMETIMES_CONSUMES
> >> diff --git
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> >> index 17955ff9774c..060273917d5d 100644
> >> ---
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> >> +++
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> >> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
> >>  #include <Library/DevicePathLib.h>
> >>  #include <Library/SecurityManagementLib.h>
> >>  #include <Library/PeCoffLib.h>
> >> +#include <Library/SafeIntLib.h>
> >>  #include <Protocol/FirmwareVolume2.h>
> >>  #include <Protocol/DevicePath.h>
> >>  #include <Protocol/BlockIo.h>
> >> diff --git
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> index 36b87e16d53d..dbc03e28c05b 100644
> >> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> +++
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
> >>    EFI_STATUS                           HashStatus;
> >>    EFI_STATUS                           DbStatus;
> >>    BOOLEAN                              IsFound;
> >> +  UINT32                               AlignedLength;
> >> +  UINT32                               Result;
> >> +  EFI_STATUS                           AddStatus;
> >> +  BOOLEAN                              IsAuthDataAssigned;
> >>
> >>    SignatureList     = NULL;
> >>    SignatureListSize = 0;
> >> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
> >>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
> >>    IsVerified        = FALSE;
> >>    IsFound           = FALSE;
> >> +  Result            = 0;
> >>
> >>    //
> >>    // Check the image type and get policy setting.
> >> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
> >>    // The first certificate starts at offset (SecDataDir->VirtualAddress) from the
> start of the file.
> >>    //
> >>    for (OffSet = SecDataDir->VirtualAddress;
> >> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >dwLength))) {
> >> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet < (SecDataDir-
> >VirtualAddress + SecDataDir->Size));) {
> >> +    IsAuthDataAssigned = FALSE;
> >>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >dwLength);
> >
> > I disagree with this patch.
> >
> > The primary reason for my disagreement is that the bug report
> > <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is inexact, and
> > so this patch tries to fix the wrong thing.
> >
> > With edk2 master at commit 65904cdbb33c, it is *not* possible to
> > overflow the OffSet variable to zero with "WinCertificate->dwLength"
> > *purely*, and cause an endless loop. Note that we have (at commit
> > 65904cdbb33c):
> >
> >   for (OffSet = SecDataDir->VirtualAddress;
> >        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >        OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >dwLength))) {
> >     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
> (WIN_CERTIFICATE) ||
> >         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
> >dwLength) {
> >       break;
> >     }
> >
> > The last sub-condition checks whether the Security Data Directory has
> > enough room left for "WinCertificate->dwLength". If not, then we break
> > out of the loop.
> >
> > If we *do* have enough room, that is:
> >
> >   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >= WinCertificate-
> >dwLength
> >
> > then we have (by adding OffSet to both sides):
> >
> >   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet + WinCertificate-
> >dwLength
> >
> > The left hand side is a known-good UINT32, and so incrementing OffSet (a
> > UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32) does not
> > cause an overflow.
> >
> > Instead, the problem is with the alignment. The "if" statement checks
> > whether we have enough room for "dwLength", but then "OffSet" is
> > advanced by "dwLength" *aligned up* to the next multiple of 8. And that
> > may indeed cause various overflows.
> >
> > Now, the main problem with the present patch is that it does not fix one
> > of those overflows. Namely, consider that "dwLength" is very close to
> > MAX_UINT32 (or even think it's exactly MAX_UINT32). Then aligning it up
> > to the next multiple of 8 will yield 0. In other words, "AlignedLength"
> > will be zero.
> >
> > And when that happens, there's going to be an infinite loop just the
> > same: "OffSet" will not be zero, but it will be *stuck*. The
> > SafeUint32Add() call at the bottom will succeed, but it will not change
> > the value of "OffSet".
> >
> > More at the bottom.
> >
> >
> >>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
> (WIN_CERTIFICATE) ||
> >>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> WinCertificate->dwLength) {
> >>        break;
> >> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
> >>        }
> >>        AuthData   = PkcsCertData->CertData;
> >>        AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData->Hdr);
> >> +      IsAuthDataAssigned = TRUE;
> >> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> >>      } else if (WinCertificate->wCertificateType == WIN_CERT_TYPE_EFI_GUID)
> {
> >>        //
> >>        // The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which is
> described in UEFI Spec.
> >> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
> >>        if (WinCertUefiGuid->Hdr.dwLength <=
> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
> >>          break;
> >>        }
> >> -      if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {
> >> -        continue;
> >> +      if (CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {
> >> +        AuthData = WinCertUefiGuid->CertData;
> >> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
> >> +        IsAuthDataAssigned = TRUE;
> >> +        HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> >>        }
> >> -      AuthData = WinCertUefiGuid->CertData;
> >> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
> >>      } else {
> >>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
> >>          break;
> >>        }
> >> -      continue;
> >>      }
> >>
> >> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> >> -    if (EFI_ERROR (HashStatus)) {
> >> -      continue;
> >> -    }
> >> -
> >> -    //
> >> -    // Check the digital signature against the revoked certificate in forbidden
> database (dbx).
> >> -    //
> >> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> >> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> >> -      IsVerified = FALSE;
> >> -      break;
> >> -    }
> >> -
> >> -    //
> >> -    // Check the digital signature against the valid certificate in allowed
> database (db).
> >> -    //
> >> -    if (!IsVerified) {
> >> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
> >> -        IsVerified = TRUE;
> >> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
> >> +      //
> >> +      // Check the digital signature against the revoked certificate in forbidden
> database (dbx).
> >> +      //
> >> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> >> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> >> +        IsVerified = FALSE;
> >> +        break;
> >>        }
> >> -    }
> >>
> >> -    //
> >> -    // Check the image's hash value.
> >> -    //
> >> -    DbStatus = IsSignatureFoundInDatabase (
> >> -                 EFI_IMAGE_SECURITY_DATABASE1,
> >> -                 mImageDigest,
> >> -                 &mCertType,
> >> -                 mImageDigestSize,
> >> -                 &IsFound
> >> -                 );
> >> -    if (EFI_ERROR (DbStatus) || IsFound) {
> >> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> >> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but %s
> hash of image is found in DBX.\n", mHashTypeStr));
> >> -      IsVerified = FALSE;
> >> -      break;
> >> -    }
> >> +      //
> >> +      // Check the digital signature against the valid certificate in allowed
> database (db).
> >> +      //
> >> +      if (!IsVerified) {
> >> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
> >> +          IsVerified = TRUE;
> >> +        }
> >> +      }
> >>
> >> -    if (!IsVerified) {
> >> +      //
> >> +      // Check the image's hash value.
> >> +      //
> >>        DbStatus = IsSignatureFoundInDatabase (
> >> -                   EFI_IMAGE_SECURITY_DATABASE,
> >> +                   EFI_IMAGE_SECURITY_DATABASE1,
> >>                     mImageDigest,
> >>                     &mCertType,
> >>                     mImageDigestSize,
> >>                     &IsFound
> >>                     );
> >> -      if (!EFI_ERROR (DbStatus) && IsFound) {
> >> -        IsVerified = TRUE;
> >> -      } else {
> >> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but
> signature is not allowed by DB and %s hash of image is not found in DB/DBX.\n",
> mHashTypeStr));
> >> +      if (EFI_ERROR (DbStatus) || IsFound) {
> >> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> >> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
> but %s hash of image is found in DBX.\n", mHashTypeStr));
> >> +        IsVerified = FALSE;
> >> +        break;
> >>        }
> >> +
> >> +      if (!IsVerified) {
> >> +        DbStatus = IsSignatureFoundInDatabase (
> >> +                     EFI_IMAGE_SECURITY_DATABASE,
> >> +                     mImageDigest,
> >> +                     &mCertType,
> >> +                     mImageDigestSize,
> >> +                     &IsFound
> >> +                     );
> >> +        if (!EFI_ERROR (DbStatus) && IsFound) {
> >> +          IsVerified = TRUE;
> >> +        } else {
> >> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but
> signature is not allowed by DB and %s hash of image is not found in DB/DBX.\n",
> mHashTypeStr));
> >> +        }
> >> +      }
> >> +    }
> >> +
> >> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
> >> +    if (EFI_ERROR (AddStatus)) {
> >> +      break;
> >>      }
> >> +    OffSet = Result;
> >>    }
> >>
> >>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
> >>
> >
> > There are other (smaller) reasons why I dislike this patch:
> >
> > - The "IsAuthDataAssigned" variable is superfluous; we could use the
> > existent "AuthData" variable (with a NULL-check and a NULL-assignment)
> > similarly.
> >
> > - The patch complicates / reorganizes the control flow needlessly. This
> > complication originates from placing the checked "OffSet" increment at
> > the bottom of the loop, which then requires the removal of all the
> > "continue" statements. But we don't need to check-and-increment at the
> > bottom. We can keep the increment inside the "for" statement, only
> > extend the *existent* room check (which I've quoted) to take the
> > alignment into account as well. If there is enough room for the
> > alignment in the security data directory, then that guarantees there
> > won't be a UINT32 overflow either.
> >
> > All in all, I'm proposing the following three patches instead. The first
> > two patches are preparation, the last patch is the fix.
> >
> > Patch#1:
> >
> >> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep 17 00:00:00
> 2001
> >> From: Laszlo Ersek <lersek@redhat.com>
> >> Date: Thu, 13 Aug 2020 19:11:39 +0200
> >> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib: extract
> >>  SecDataDirEnd, SecDataDirLeft
> >>
> >> The following two quantities:
> >>
> >>   SecDataDir->VirtualAddress + SecDataDir->Size
> >>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
> >>
> >> are used multiple times in DxeImageVerificationHandler(). Introduce helper
> >> variables for them: "SecDataDirEnd" and "SecDataDirLeft", respectively.
> >> This saves us multiple calculations and significantly simplifies the code.
> >>
> >> Note that all three summands above have type UINT32, therefore the new
> >> variables are also of type UINT32.
> >>
> >> This patch does not change behavior.
> >>
> >> (Note that the code already handles the case when the
> >>
> >>   SecDataDir->VirtualAddress + SecDataDir->Size
> >>
> >> UINT32 addition overflows -- namely, in that case, the certificate loop is
> >> never entered, and the corruption check right after the loop fires.)
> >>
> >> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >> ---
> >>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c | 12
> ++++++++----
> >>  1 file changed, 8 insertions(+), 4 deletions(-)
> >>
> >> diff --git
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> index 36b87e16d53d..8761980c88aa 100644
> >> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> +++
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
> >>    UINT8                                *AuthData;
> >>    UINTN                                AuthDataSize;
> >>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
> >> +  UINT32                               SecDataDirEnd;
> >> +  UINT32                               SecDataDirLeft;
> >>    UINT32                               OffSet;
> >>    CHAR16                               *NameStr;
> >>    RETURN_STATUS                        PeCoffStatus;
> >> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
> >>    // "Attribute Certificate Table".
> >>    // The first certificate starts at offset (SecDataDir->VirtualAddress) from the
> start of the file.
> >>    //
> >> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
> >>    for (OffSet = SecDataDir->VirtualAddress;
> >> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >> +       OffSet < SecDataDirEnd;
> >>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >dwLength))) {
> >>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
> (WIN_CERTIFICATE) ||
> >> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> WinCertificate->dwLength) {
> >> +    SecDataDirLeft = SecDataDirEnd - OffSet;
> >> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
> >> +        SecDataDirLeft < WinCertificate->dwLength) {
> >>        break;
> >>      }
> >>
> >> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
> >>      }
> >>    }
> >>
> >> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
> >> +  if (OffSet != SecDataDirEnd) {
> >>      //
> >>      // The Size in Certificate Table or the attribute certificate table is corrupted.
> >>      //
> >> --
> >> 2.19.1.3.g30247aa5d201
> >>
> >
> > Patch#2:
> >
> >> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep 17 00:00:00
> 2001
> >> From: Laszlo Ersek <lersek@redhat.com>
> >> Date: Thu, 13 Aug 2020 19:19:06 +0200
> >> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib: assign
> >>  WinCertificate after size check
> >>
> >> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) check only
> >> guards the de-referencing of the "WinCertificate" pointer. It does not
> >> guard the calculation of hte pointer itself:
> >>
> >>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>
> >> This is wrong; if we don't know for sure that we have enough room for a
> >> WIN_CERTIFICATE, then even creating such a pointer, not just
> >> de-referencing it, may invoke undefined behavior.
> >>
> >> Move the pointer calculation after the size check.
> >>
> >> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >> ---
> >>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c | 8
> +++++---
> >>  1 file changed, 5 insertions(+), 3 deletions(-)
> >>
> >> diff --git
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> index 8761980c88aa..461ed7cfb5ac 100644
> >> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> +++
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
> >>    for (OffSet = SecDataDir->VirtualAddress;
> >>         OffSet < SecDataDirEnd;
> >>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >dwLength))) {
> >> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>      SecDataDirLeft = SecDataDirEnd - OffSet;
> >> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
> >> -        SecDataDirLeft < WinCertificate->dwLength) {
> >> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
> >> +      break;
> >> +    }
> >> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >> +    if (SecDataDirLeft < WinCertificate->dwLength) {
> >>        break;
> >>      }
> >>
> >> --
> >> 2.19.1.3.g30247aa5d201
> >>
> >
> > Patch#3:
> >
> >> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17 00:00:00
> 2001
> >> From: Laszlo Ersek <lersek@redhat.com>
> >> Date: Thu, 13 Aug 2020 19:34:33 +0200
> >> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch alignment
> >>  overflow (CVE-2019-14562)
> >>
> >> The DxeImageVerificationHandler() function currently checks whether
> >> "SecDataDir" has enough room for "WinCertificate->dwLength". However,
> for
> >> advancing "OffSet", "WinCertificate->dwLength" is aligned to the next
> >> multiple of 8. If "WinCertificate->dwLength" is large enough, the
> >> alignment will return 0, and "OffSet" will be stuck at the same value.
> >>
> >> Check whether "SecDataDir" has room left for both
> >> "WinCertificate->dwLength" and the alignment.
> >>
> >> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >> ---
> >>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c | 4
> +++-
> >>  1 file changed, 3 insertions(+), 1 deletion(-)
> >>
> >> diff --git
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> index 461ed7cfb5ac..e38eb981b7a0 100644
> >> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> +++
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
> >>        break;
> >>      }
> >>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >> -    if (SecDataDirLeft < WinCertificate->dwLength) {
> >> +    if (SecDataDirLeft < WinCertificate->dwLength ||
> >> +        (SecDataDirLeft - WinCertificate->dwLength <
> >> +         ALIGN_SIZE (WinCertificate->dwLength))) {
> >>        break;
> >>      }
> >>
> >> --
> >> 2.19.1.3.g30247aa5d201
> >>
> >
> > If Wenyi and the reviewers are OK with these patches, I can submit them
> > as a standalone patch series.
> >
> > Note that I do not have any reproducer for the issue; the best testing
> > that I could offer would be some light-weight Secure Boot regression
> > tests.
> >
> > Thanks
> > Laszlo
> >
> >
> > .
> >
> 
> 
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-14  8:53       ` [edk2-devel] " Yao, Jiewen
@ 2020-08-14 11:29         ` wenyi,xie
  2020-08-17 16:52         ` Laszlo Ersek
  1 sibling, 0 replies; 32+ messages in thread
From: wenyi,xie @ 2020-08-14 11:29 UTC (permalink / raw)
  To: Yao, Jiewen, devel@edk2.groups.io, Laszlo Ersek, Wang, Jian J
  Cc: huangming23@huawei.com, songdongkuang@huawei.com



On 2020/8/14 16:53, Yao, Jiewen wrote:
>> To Jiewen,
>> Sorry, I don't have environment to reproduce the issue.
> 
> Please help me understand, if you don’t have environment to reproduce the issue, how do you guarantee that your patch does fix the problem and we don’t have any other vulnerabilities?

Hi, Jiewen,
You're right, as I can't reproduce the issue, I can't guarantee my patches can fix the problem.
And as Laszlo analyzed, my patches can't solve overflow issue indeed.

Sincerely
Wenyi
> 
> Thank you
> Yao Jiewen
> 
> 
>> -----Original Message-----
>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of wenyi,xie
>> via groups.io
>> Sent: Friday, August 14, 2020 3:54 PM
>> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io; Yao, Jiewen
>> <jiewen.yao@intel.com>; Wang, Jian J <jian.j.wang@intel.com>
>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>
>> To Laszlo,
>> Thank you for your detailed description, I agree with what you analyzed and I'm
>> OK with your patches, it's
>> correct and much simpler.
>>
>> To Jiewen,
>> Sorry, I don't have environment to reproduce the issue.
>>
>> Thanks
>> Wenyi
>>
>> On 2020/8/14 2:50, Laszlo Ersek wrote:
>>> On 08/13/20 13:55, Wenyi Xie wrote:
>>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
>>>>
>>>> There is an integer overflow vulnerability in DxeImageVerificationHandler
>>>> function when parsing the PE files attribute certificate table. In cases
>>>> where WinCertificate->dwLength is sufficiently large, it's possible to
>>>> overflow Offset back to 0 causing an endless loop.
>>>>
>>>> Check offset inbetween VirtualAddress and VirtualAddress + Size.
>>>> Using SafeintLib to do offset addition with result check.
>>>>
>>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>>>> Cc: Jian J Wang <jian.j.wang@intel.com>
>>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
>>>> ---
>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf |
>> 1 +
>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h   |
>> 1 +
>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c   |
>> 111 +++++++++++---------
>>>>  3 files changed, 63 insertions(+), 50 deletions(-)
>>>>
>>>> diff --git
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
>>>> index 1e1a639857e0..a7ac4830b3d4 100644
>>>> ---
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
>>>> +++
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
>>>> @@ -53,6 +53,7 @@ [LibraryClasses]
>>>>    SecurityManagementLib
>>>>    PeCoffLib
>>>>    TpmMeasurementLib
>>>> +  SafeIntLib
>>>>
>>>>  [Protocols]
>>>>    gEfiFirmwareVolume2ProtocolGuid       ## SOMETIMES_CONSUMES
>>>> diff --git
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
>>>> index 17955ff9774c..060273917d5d 100644
>>>> ---
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
>>>> +++
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
>>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
>>>>  #include <Library/DevicePathLib.h>
>>>>  #include <Library/SecurityManagementLib.h>
>>>>  #include <Library/PeCoffLib.h>
>>>> +#include <Library/SafeIntLib.h>
>>>>  #include <Protocol/FirmwareVolume2.h>
>>>>  #include <Protocol/DevicePath.h>
>>>>  #include <Protocol/BlockIo.h>
>>>> diff --git
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> index 36b87e16d53d..dbc03e28c05b 100644
>>>> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> +++
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
>>>>    EFI_STATUS                           HashStatus;
>>>>    EFI_STATUS                           DbStatus;
>>>>    BOOLEAN                              IsFound;
>>>> +  UINT32                               AlignedLength;
>>>> +  UINT32                               Result;
>>>> +  EFI_STATUS                           AddStatus;
>>>> +  BOOLEAN                              IsAuthDataAssigned;
>>>>
>>>>    SignatureList     = NULL;
>>>>    SignatureListSize = 0;
>>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
>>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
>>>>    IsVerified        = FALSE;
>>>>    IsFound           = FALSE;
>>>> +  Result            = 0;
>>>>
>>>>    //
>>>>    // Check the image type and get policy setting.
>>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
>>>>    // The first certificate starts at offset (SecDataDir->VirtualAddress) from the
>> start of the file.
>>>>    //
>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>> dwLength))) {
>>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet < (SecDataDir-
>>> VirtualAddress + SecDataDir->Size));) {
>>>> +    IsAuthDataAssigned = FALSE;
>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>> dwLength);
>>>
>>> I disagree with this patch.
>>>
>>> The primary reason for my disagreement is that the bug report
>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is inexact, and
>>> so this patch tries to fix the wrong thing.
>>>
>>> With edk2 master at commit 65904cdbb33c, it is *not* possible to
>>> overflow the OffSet variable to zero with "WinCertificate->dwLength"
>>> *purely*, and cause an endless loop. Note that we have (at commit
>>> 65904cdbb33c):
>>>
>>>   for (OffSet = SecDataDir->VirtualAddress;
>>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>> dwLength))) {
>>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>> (WIN_CERTIFICATE) ||
>>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
>>> dwLength) {
>>>       break;
>>>     }
>>>
>>> The last sub-condition checks whether the Security Data Directory has
>>> enough room left for "WinCertificate->dwLength". If not, then we break
>>> out of the loop.
>>>
>>> If we *do* have enough room, that is:
>>>
>>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >= WinCertificate-
>>> dwLength
>>>
>>> then we have (by adding OffSet to both sides):
>>>
>>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet + WinCertificate-
>>> dwLength
>>>
>>> The left hand side is a known-good UINT32, and so incrementing OffSet (a
>>> UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32) does not
>>> cause an overflow.
>>>
>>> Instead, the problem is with the alignment. The "if" statement checks
>>> whether we have enough room for "dwLength", but then "OffSet" is
>>> advanced by "dwLength" *aligned up* to the next multiple of 8. And that
>>> may indeed cause various overflows.
>>>
>>> Now, the main problem with the present patch is that it does not fix one
>>> of those overflows. Namely, consider that "dwLength" is very close to
>>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then aligning it up
>>> to the next multiple of 8 will yield 0. In other words, "AlignedLength"
>>> will be zero.
>>>
>>> And when that happens, there's going to be an infinite loop just the
>>> same: "OffSet" will not be zero, but it will be *stuck*. The
>>> SafeUint32Add() call at the bottom will succeed, but it will not change
>>> the value of "OffSet".
>>>
>>> More at the bottom.
>>>
>>>
>>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>> (WIN_CERTIFICATE) ||
>>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>> WinCertificate->dwLength) {
>>>>        break;
>>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
>>>>        }
>>>>        AuthData   = PkcsCertData->CertData;
>>>>        AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData->Hdr);
>>>> +      IsAuthDataAssigned = TRUE;
>>>> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>      } else if (WinCertificate->wCertificateType == WIN_CERT_TYPE_EFI_GUID)
>> {
>>>>        //
>>>>        // The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which is
>> described in UEFI Spec.
>>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
>>>>        if (WinCertUefiGuid->Hdr.dwLength <=
>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
>>>>          break;
>>>>        }
>>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {
>>>> -        continue;
>>>> +      if (CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {
>>>> +        AuthData = WinCertUefiGuid->CertData;
>>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>> +        IsAuthDataAssigned = TRUE;
>>>> +        HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>        }
>>>> -      AuthData = WinCertUefiGuid->CertData;
>>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>      } else {
>>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
>>>>          break;
>>>>        }
>>>> -      continue;
>>>>      }
>>>>
>>>> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>> -    if (EFI_ERROR (HashStatus)) {
>>>> -      continue;
>>>> -    }
>>>> -
>>>> -    //
>>>> -    // Check the digital signature against the revoked certificate in forbidden
>> database (dbx).
>>>> -    //
>>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>> -      IsVerified = FALSE;
>>>> -      break;
>>>> -    }
>>>> -
>>>> -    //
>>>> -    // Check the digital signature against the valid certificate in allowed
>> database (db).
>>>> -    //
>>>> -    if (!IsVerified) {
>>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>> -        IsVerified = TRUE;
>>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
>>>> +      //
>>>> +      // Check the digital signature against the revoked certificate in forbidden
>> database (dbx).
>>>> +      //
>>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>> +        IsVerified = FALSE;
>>>> +        break;
>>>>        }
>>>> -    }
>>>>
>>>> -    //
>>>> -    // Check the image's hash value.
>>>> -    //
>>>> -    DbStatus = IsSignatureFoundInDatabase (
>>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
>>>> -                 mImageDigest,
>>>> -                 &mCertType,
>>>> -                 mImageDigestSize,
>>>> -                 &IsFound
>>>> -                 );
>>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but %s
>> hash of image is found in DBX.\n", mHashTypeStr));
>>>> -      IsVerified = FALSE;
>>>> -      break;
>>>> -    }
>>>> +      //
>>>> +      // Check the digital signature against the valid certificate in allowed
>> database (db).
>>>> +      //
>>>> +      if (!IsVerified) {
>>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>> +          IsVerified = TRUE;
>>>> +        }
>>>> +      }
>>>>
>>>> -    if (!IsVerified) {
>>>> +      //
>>>> +      // Check the image's hash value.
>>>> +      //
>>>>        DbStatus = IsSignatureFoundInDatabase (
>>>> -                   EFI_IMAGE_SECURITY_DATABASE,
>>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
>>>>                     mImageDigest,
>>>>                     &mCertType,
>>>>                     mImageDigestSize,
>>>>                     &IsFound
>>>>                     );
>>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
>>>> -        IsVerified = TRUE;
>>>> -      } else {
>>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but
>> signature is not allowed by DB and %s hash of image is not found in DB/DBX.\n",
>> mHashTypeStr));
>>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
>> but %s hash of image is found in DBX.\n", mHashTypeStr));
>>>> +        IsVerified = FALSE;
>>>> +        break;
>>>>        }
>>>> +
>>>> +      if (!IsVerified) {
>>>> +        DbStatus = IsSignatureFoundInDatabase (
>>>> +                     EFI_IMAGE_SECURITY_DATABASE,
>>>> +                     mImageDigest,
>>>> +                     &mCertType,
>>>> +                     mImageDigestSize,
>>>> +                     &IsFound
>>>> +                     );
>>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
>>>> +          IsVerified = TRUE;
>>>> +        } else {
>>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but
>> signature is not allowed by DB and %s hash of image is not found in DB/DBX.\n",
>> mHashTypeStr));
>>>> +        }
>>>> +      }
>>>> +    }
>>>> +
>>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
>>>> +    if (EFI_ERROR (AddStatus)) {
>>>> +      break;
>>>>      }
>>>> +    OffSet = Result;
>>>>    }
>>>>
>>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
>>>>
>>>
>>> There are other (smaller) reasons why I dislike this patch:
>>>
>>> - The "IsAuthDataAssigned" variable is superfluous; we could use the
>>> existent "AuthData" variable (with a NULL-check and a NULL-assignment)
>>> similarly.
>>>
>>> - The patch complicates / reorganizes the control flow needlessly. This
>>> complication originates from placing the checked "OffSet" increment at
>>> the bottom of the loop, which then requires the removal of all the
>>> "continue" statements. But we don't need to check-and-increment at the
>>> bottom. We can keep the increment inside the "for" statement, only
>>> extend the *existent* room check (which I've quoted) to take the
>>> alignment into account as well. If there is enough room for the
>>> alignment in the security data directory, then that guarantees there
>>> won't be a UINT32 overflow either.
>>>
>>> All in all, I'm proposing the following three patches instead. The first
>>> two patches are preparation, the last patch is the fix.
>>>
>>> Patch#1:
>>>
>>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep 17 00:00:00
>> 2001
>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
>>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib: extract
>>>>  SecDataDirEnd, SecDataDirLeft
>>>>
>>>> The following two quantities:
>>>>
>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
>>>>
>>>> are used multiple times in DxeImageVerificationHandler(). Introduce helper
>>>> variables for them: "SecDataDirEnd" and "SecDataDirLeft", respectively.
>>>> This saves us multiple calculations and significantly simplifies the code.
>>>>
>>>> Note that all three summands above have type UINT32, therefore the new
>>>> variables are also of type UINT32.
>>>>
>>>> This patch does not change behavior.
>>>>
>>>> (Note that the code already handles the case when the
>>>>
>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>
>>>> UINT32 addition overflows -- namely, in that case, the certificate loop is
>>>> never entered, and the corruption check right after the loop fires.)
>>>>
>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>> ---
>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c | 12
>> ++++++++----
>>>>  1 file changed, 8 insertions(+), 4 deletions(-)
>>>>
>>>> diff --git
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> index 36b87e16d53d..8761980c88aa 100644
>>>> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> +++
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
>>>>    UINT8                                *AuthData;
>>>>    UINTN                                AuthDataSize;
>>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
>>>> +  UINT32                               SecDataDirEnd;
>>>> +  UINT32                               SecDataDirLeft;
>>>>    UINT32                               OffSet;
>>>>    CHAR16                               *NameStr;
>>>>    RETURN_STATUS                        PeCoffStatus;
>>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
>>>>    // "Attribute Certificate Table".
>>>>    // The first certificate starts at offset (SecDataDir->VirtualAddress) from the
>> start of the file.
>>>>    //
>>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>> +       OffSet < SecDataDirEnd;
>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>> dwLength))) {
>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>> (WIN_CERTIFICATE) ||
>>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>> WinCertificate->dwLength) {
>>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>> +        SecDataDirLeft < WinCertificate->dwLength) {
>>>>        break;
>>>>      }
>>>>
>>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
>>>>      }
>>>>    }
>>>>
>>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
>>>> +  if (OffSet != SecDataDirEnd) {
>>>>      //
>>>>      // The Size in Certificate Table or the attribute certificate table is corrupted.
>>>>      //
>>>> --
>>>> 2.19.1.3.g30247aa5d201
>>>>
>>>
>>> Patch#2:
>>>
>>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep 17 00:00:00
>> 2001
>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
>>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib: assign
>>>>  WinCertificate after size check
>>>>
>>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) check only
>>>> guards the de-referencing of the "WinCertificate" pointer. It does not
>>>> guard the calculation of hte pointer itself:
>>>>
>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>
>>>> This is wrong; if we don't know for sure that we have enough room for a
>>>> WIN_CERTIFICATE, then even creating such a pointer, not just
>>>> de-referencing it, may invoke undefined behavior.
>>>>
>>>> Move the pointer calculation after the size check.
>>>>
>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>> ---
>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c | 8
>> +++++---
>>>>  1 file changed, 5 insertions(+), 3 deletions(-)
>>>>
>>>> diff --git
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> index 8761980c88aa..461ed7cfb5ac 100644
>>>> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> +++
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>         OffSet < SecDataDirEnd;
>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>> dwLength))) {
>>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
>>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>> -        SecDataDirLeft < WinCertificate->dwLength) {
>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
>>>> +      break;
>>>> +    }
>>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>        break;
>>>>      }
>>>>
>>>> --
>>>> 2.19.1.3.g30247aa5d201
>>>>
>>>
>>> Patch#3:
>>>
>>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17 00:00:00
>> 2001
>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
>>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch alignment
>>>>  overflow (CVE-2019-14562)
>>>>
>>>> The DxeImageVerificationHandler() function currently checks whether
>>>> "SecDataDir" has enough room for "WinCertificate->dwLength". However,
>> for
>>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to the next
>>>> multiple of 8. If "WinCertificate->dwLength" is large enough, the
>>>> alignment will return 0, and "OffSet" will be stuck at the same value.
>>>>
>>>> Check whether "SecDataDir" has room left for both
>>>> "WinCertificate->dwLength" and the alignment.
>>>>
>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>> ---
>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c | 4
>> +++-
>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> index 461ed7cfb5ac..e38eb981b7a0 100644
>>>> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> +++
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
>>>>        break;
>>>>      }
>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
>>>> +        (SecDataDirLeft - WinCertificate->dwLength <
>>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
>>>>        break;
>>>>      }
>>>>
>>>> --
>>>> 2.19.1.3.g30247aa5d201
>>>>
>>>
>>> If Wenyi and the reviewers are OK with these patches, I can submit them
>>> as a standalone patch series.
>>>
>>> Note that I do not have any reproducer for the issue; the best testing
>>> that I could offer would be some light-weight Secure Boot regression
>>> tests.
>>>
>>> Thanks
>>> Laszlo
>>>
>>>
>>> .
>>>
>>
>>
>> 
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-14  8:53       ` [edk2-devel] " Yao, Jiewen
  2020-08-14 11:29         ` wenyi,xie
@ 2020-08-17 16:52         ` Laszlo Ersek
  2020-08-17 23:23           ` Yao, Jiewen
  2020-08-18  2:10           ` Wang, Jian J
  1 sibling, 2 replies; 32+ messages in thread
From: Laszlo Ersek @ 2020-08-17 16:52 UTC (permalink / raw)
  To: Yao, Jiewen, devel@edk2.groups.io, xiewenyi2@huawei.com,
	Wang, Jian J
  Cc: huangming23@huawei.com, songdongkuang@huawei.com

Hi Jiewen,

On 08/14/20 10:53, Yao, Jiewen wrote:
>> To Jiewen,
>> Sorry, I don't have environment to reproduce the issue.
> 
> Please help me understand, if you don’t have environment to reproduce the issue, how do you guarantee that your patch does fix the problem and we don’t have any other vulnerabilities?

The original bug report in
<https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is seriously
lacking. It does not go into detail about the alleged integer overflow.
It does not quote the code, does not explain the control flow, does not
identify the exact edk2 commit at which the vulnerability exists.

The bug report also does not offer a reproducer.

Additionally, the exact statement that the bug report does make, namely

  it's possible to overflow Offset back to 0 causing an endless loop

is wrong (as far as I can tell anyway). It is not "OffSet" that can be
overflowed to zero, but the *addend* that is added to OffSet can be
overflowed to zero. Therefore the infinite loop will arise because
OffSet remains stuck at its present value, and not because OffSet will
be re-set to zero.

For the reasons, we can only speculate as to what the actual problem is,
unless Jian decides to join the discussion and clarifies what he had in
mind originally.

My understanding (or even "reconstruction") of the vulnerability is
described above, and in the patches that I proposed.

We can write a patch based on code analysis. It's possible to identify
integer overflows based on code analysis, and it's possible to verify
the correctness of fixes by code review. Obviously testing is always
good, but many times, constructing reproducers for such issues that were
found by code review, is difficult and time consuming. We can say that
we don't fix vulnerabilities without reproducers, or we can say that we
make an effort to fix them even if all we have is code analysis (and not
a reproducer).

So the above paragraph concerns "correctness". Regarding "completeness",
I guarantee you that this patch does not fix *all* problems related to
PE parsing. (See the other BZ tickets.) It does fix *one* issue with PE
parsing. We can say that we try to fix such issues gradually (give
different CVE numbers to different issues, and address them one at a
time), or we can say that we rewrite PE parsing from the ground up.
(BTW: I have seriously attempted that in the past, and I gave up,
because the PE format is FUBAR.)

In summary:

- the problem statement is unclear,

- it seems like there is indeed an integer overflow problem in the
SecDataDir parsing loop, but it's uncertain whether the bug reporter had
exactly that in mind

- PE parsing is guaranteed to have other vulnerabilities elsewhere in
edk2, but I'm currently unaware of other such issues in
DxeImageVerificationLib specifically

- even if there are other such problems (in DxeImageVerificationLib or
elswehere), fixing this bug that we know about is likely worthwhile

- for many such bugs, constructing a reproducer is difficult and time
consuming; code analysis, and *regression-testing* are frequently the
only tools we have. That doesn't mean we should ignore this class of bugs.

(Fixing integer overflows retro-actively is more difficult than writing
overflow-free code in the first place, but that ship has sailed; so we
can only fight these bugs incrementally now, unless we can rewrite PE
parsing with a new data structure from the ground up. Again I tried that
and gave up, because the spec is not public, and what I did manage to
learn about PE, showed that it was insanely over-engineered. I'm not
saying that other binary / executable formats are better, of course.)

Please check out my patches (inlined elsewhere in this thread), and
comment whether you'd like me to post them to the list as a standalone
series.

Jian: it wouldn't hurt if you commented as well.

Thanks
Laszlo

>> -----Original Message-----
>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of wenyi,xie
>> via groups.io
>> Sent: Friday, August 14, 2020 3:54 PM
>> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io; Yao, Jiewen
>> <jiewen.yao@intel.com>; Wang, Jian J <jian.j.wang@intel.com>
>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>
>> To Laszlo,
>> Thank you for your detailed description, I agree with what you analyzed and I'm
>> OK with your patches, it's
>> correct and much simpler.
>>
>> To Jiewen,
>> Sorry, I don't have environment to reproduce the issue.
>>
>> Thanks
>> Wenyi
>>
>> On 2020/8/14 2:50, Laszlo Ersek wrote:
>>> On 08/13/20 13:55, Wenyi Xie wrote:
>>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
>>>>
>>>> There is an integer overflow vulnerability in DxeImageVerificationHandler
>>>> function when parsing the PE files attribute certificate table. In cases
>>>> where WinCertificate->dwLength is sufficiently large, it's possible to
>>>> overflow Offset back to 0 causing an endless loop.
>>>>
>>>> Check offset inbetween VirtualAddress and VirtualAddress + Size.
>>>> Using SafeintLib to do offset addition with result check.
>>>>
>>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>>>> Cc: Jian J Wang <jian.j.wang@intel.com>
>>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
>>>> ---
>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf |
>> 1 +
>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h   |
>> 1 +
>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c   |
>> 111 +++++++++++---------
>>>>  3 files changed, 63 insertions(+), 50 deletions(-)
>>>>
>>>> diff --git
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
>>>> index 1e1a639857e0..a7ac4830b3d4 100644
>>>> ---
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
>>>> +++
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
>>>> @@ -53,6 +53,7 @@ [LibraryClasses]
>>>>    SecurityManagementLib
>>>>    PeCoffLib
>>>>    TpmMeasurementLib
>>>> +  SafeIntLib
>>>>
>>>>  [Protocols]
>>>>    gEfiFirmwareVolume2ProtocolGuid       ## SOMETIMES_CONSUMES
>>>> diff --git
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
>>>> index 17955ff9774c..060273917d5d 100644
>>>> ---
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
>>>> +++
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
>>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
>>>>  #include <Library/DevicePathLib.h>
>>>>  #include <Library/SecurityManagementLib.h>
>>>>  #include <Library/PeCoffLib.h>
>>>> +#include <Library/SafeIntLib.h>
>>>>  #include <Protocol/FirmwareVolume2.h>
>>>>  #include <Protocol/DevicePath.h>
>>>>  #include <Protocol/BlockIo.h>
>>>> diff --git
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> index 36b87e16d53d..dbc03e28c05b 100644
>>>> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> +++
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
>>>>    EFI_STATUS                           HashStatus;
>>>>    EFI_STATUS                           DbStatus;
>>>>    BOOLEAN                              IsFound;
>>>> +  UINT32                               AlignedLength;
>>>> +  UINT32                               Result;
>>>> +  EFI_STATUS                           AddStatus;
>>>> +  BOOLEAN                              IsAuthDataAssigned;
>>>>
>>>>    SignatureList     = NULL;
>>>>    SignatureListSize = 0;
>>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
>>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
>>>>    IsVerified        = FALSE;
>>>>    IsFound           = FALSE;
>>>> +  Result            = 0;
>>>>
>>>>    //
>>>>    // Check the image type and get policy setting.
>>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
>>>>    // The first certificate starts at offset (SecDataDir->VirtualAddress) from the
>> start of the file.
>>>>    //
>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>> dwLength))) {
>>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet < (SecDataDir-
>>> VirtualAddress + SecDataDir->Size));) {
>>>> +    IsAuthDataAssigned = FALSE;
>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>> dwLength);
>>>
>>> I disagree with this patch.
>>>
>>> The primary reason for my disagreement is that the bug report
>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is inexact, and
>>> so this patch tries to fix the wrong thing.
>>>
>>> With edk2 master at commit 65904cdbb33c, it is *not* possible to
>>> overflow the OffSet variable to zero with "WinCertificate->dwLength"
>>> *purely*, and cause an endless loop. Note that we have (at commit
>>> 65904cdbb33c):
>>>
>>>   for (OffSet = SecDataDir->VirtualAddress;
>>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>> dwLength))) {
>>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>> (WIN_CERTIFICATE) ||
>>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
>>> dwLength) {
>>>       break;
>>>     }
>>>
>>> The last sub-condition checks whether the Security Data Directory has
>>> enough room left for "WinCertificate->dwLength". If not, then we break
>>> out of the loop.
>>>
>>> If we *do* have enough room, that is:
>>>
>>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >= WinCertificate-
>>> dwLength
>>>
>>> then we have (by adding OffSet to both sides):
>>>
>>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet + WinCertificate-
>>> dwLength
>>>
>>> The left hand side is a known-good UINT32, and so incrementing OffSet (a
>>> UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32) does not
>>> cause an overflow.
>>>
>>> Instead, the problem is with the alignment. The "if" statement checks
>>> whether we have enough room for "dwLength", but then "OffSet" is
>>> advanced by "dwLength" *aligned up* to the next multiple of 8. And that
>>> may indeed cause various overflows.
>>>
>>> Now, the main problem with the present patch is that it does not fix one
>>> of those overflows. Namely, consider that "dwLength" is very close to
>>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then aligning it up
>>> to the next multiple of 8 will yield 0. In other words, "AlignedLength"
>>> will be zero.
>>>
>>> And when that happens, there's going to be an infinite loop just the
>>> same: "OffSet" will not be zero, but it will be *stuck*. The
>>> SafeUint32Add() call at the bottom will succeed, but it will not change
>>> the value of "OffSet".
>>>
>>> More at the bottom.
>>>
>>>
>>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>> (WIN_CERTIFICATE) ||
>>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>> WinCertificate->dwLength) {
>>>>        break;
>>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
>>>>        }
>>>>        AuthData   = PkcsCertData->CertData;
>>>>        AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData->Hdr);
>>>> +      IsAuthDataAssigned = TRUE;
>>>> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>      } else if (WinCertificate->wCertificateType == WIN_CERT_TYPE_EFI_GUID)
>> {
>>>>        //
>>>>        // The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which is
>> described in UEFI Spec.
>>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
>>>>        if (WinCertUefiGuid->Hdr.dwLength <=
>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
>>>>          break;
>>>>        }
>>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {
>>>> -        continue;
>>>> +      if (CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {
>>>> +        AuthData = WinCertUefiGuid->CertData;
>>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>> +        IsAuthDataAssigned = TRUE;
>>>> +        HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>        }
>>>> -      AuthData = WinCertUefiGuid->CertData;
>>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>      } else {
>>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
>>>>          break;
>>>>        }
>>>> -      continue;
>>>>      }
>>>>
>>>> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>> -    if (EFI_ERROR (HashStatus)) {
>>>> -      continue;
>>>> -    }
>>>> -
>>>> -    //
>>>> -    // Check the digital signature against the revoked certificate in forbidden
>> database (dbx).
>>>> -    //
>>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>> -      IsVerified = FALSE;
>>>> -      break;
>>>> -    }
>>>> -
>>>> -    //
>>>> -    // Check the digital signature against the valid certificate in allowed
>> database (db).
>>>> -    //
>>>> -    if (!IsVerified) {
>>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>> -        IsVerified = TRUE;
>>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
>>>> +      //
>>>> +      // Check the digital signature against the revoked certificate in forbidden
>> database (dbx).
>>>> +      //
>>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>> +        IsVerified = FALSE;
>>>> +        break;
>>>>        }
>>>> -    }
>>>>
>>>> -    //
>>>> -    // Check the image's hash value.
>>>> -    //
>>>> -    DbStatus = IsSignatureFoundInDatabase (
>>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
>>>> -                 mImageDigest,
>>>> -                 &mCertType,
>>>> -                 mImageDigestSize,
>>>> -                 &IsFound
>>>> -                 );
>>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but %s
>> hash of image is found in DBX.\n", mHashTypeStr));
>>>> -      IsVerified = FALSE;
>>>> -      break;
>>>> -    }
>>>> +      //
>>>> +      // Check the digital signature against the valid certificate in allowed
>> database (db).
>>>> +      //
>>>> +      if (!IsVerified) {
>>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>> +          IsVerified = TRUE;
>>>> +        }
>>>> +      }
>>>>
>>>> -    if (!IsVerified) {
>>>> +      //
>>>> +      // Check the image's hash value.
>>>> +      //
>>>>        DbStatus = IsSignatureFoundInDatabase (
>>>> -                   EFI_IMAGE_SECURITY_DATABASE,
>>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
>>>>                     mImageDigest,
>>>>                     &mCertType,
>>>>                     mImageDigestSize,
>>>>                     &IsFound
>>>>                     );
>>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
>>>> -        IsVerified = TRUE;
>>>> -      } else {
>>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but
>> signature is not allowed by DB and %s hash of image is not found in DB/DBX.\n",
>> mHashTypeStr));
>>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
>> but %s hash of image is found in DBX.\n", mHashTypeStr));
>>>> +        IsVerified = FALSE;
>>>> +        break;
>>>>        }
>>>> +
>>>> +      if (!IsVerified) {
>>>> +        DbStatus = IsSignatureFoundInDatabase (
>>>> +                     EFI_IMAGE_SECURITY_DATABASE,
>>>> +                     mImageDigest,
>>>> +                     &mCertType,
>>>> +                     mImageDigestSize,
>>>> +                     &IsFound
>>>> +                     );
>>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
>>>> +          IsVerified = TRUE;
>>>> +        } else {
>>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but
>> signature is not allowed by DB and %s hash of image is not found in DB/DBX.\n",
>> mHashTypeStr));
>>>> +        }
>>>> +      }
>>>> +    }
>>>> +
>>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
>>>> +    if (EFI_ERROR (AddStatus)) {
>>>> +      break;
>>>>      }
>>>> +    OffSet = Result;
>>>>    }
>>>>
>>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
>>>>
>>>
>>> There are other (smaller) reasons why I dislike this patch:
>>>
>>> - The "IsAuthDataAssigned" variable is superfluous; we could use the
>>> existent "AuthData" variable (with a NULL-check and a NULL-assignment)
>>> similarly.
>>>
>>> - The patch complicates / reorganizes the control flow needlessly. This
>>> complication originates from placing the checked "OffSet" increment at
>>> the bottom of the loop, which then requires the removal of all the
>>> "continue" statements. But we don't need to check-and-increment at the
>>> bottom. We can keep the increment inside the "for" statement, only
>>> extend the *existent* room check (which I've quoted) to take the
>>> alignment into account as well. If there is enough room for the
>>> alignment in the security data directory, then that guarantees there
>>> won't be a UINT32 overflow either.
>>>
>>> All in all, I'm proposing the following three patches instead. The first
>>> two patches are preparation, the last patch is the fix.
>>>
>>> Patch#1:
>>>
>>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep 17 00:00:00
>> 2001
>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
>>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib: extract
>>>>  SecDataDirEnd, SecDataDirLeft
>>>>
>>>> The following two quantities:
>>>>
>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
>>>>
>>>> are used multiple times in DxeImageVerificationHandler(). Introduce helper
>>>> variables for them: "SecDataDirEnd" and "SecDataDirLeft", respectively.
>>>> This saves us multiple calculations and significantly simplifies the code.
>>>>
>>>> Note that all three summands above have type UINT32, therefore the new
>>>> variables are also of type UINT32.
>>>>
>>>> This patch does not change behavior.
>>>>
>>>> (Note that the code already handles the case when the
>>>>
>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>
>>>> UINT32 addition overflows -- namely, in that case, the certificate loop is
>>>> never entered, and the corruption check right after the loop fires.)
>>>>
>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>> ---
>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c | 12
>> ++++++++----
>>>>  1 file changed, 8 insertions(+), 4 deletions(-)
>>>>
>>>> diff --git
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> index 36b87e16d53d..8761980c88aa 100644
>>>> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> +++
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
>>>>    UINT8                                *AuthData;
>>>>    UINTN                                AuthDataSize;
>>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
>>>> +  UINT32                               SecDataDirEnd;
>>>> +  UINT32                               SecDataDirLeft;
>>>>    UINT32                               OffSet;
>>>>    CHAR16                               *NameStr;
>>>>    RETURN_STATUS                        PeCoffStatus;
>>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
>>>>    // "Attribute Certificate Table".
>>>>    // The first certificate starts at offset (SecDataDir->VirtualAddress) from the
>> start of the file.
>>>>    //
>>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>> +       OffSet < SecDataDirEnd;
>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>> dwLength))) {
>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>> (WIN_CERTIFICATE) ||
>>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>> WinCertificate->dwLength) {
>>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>> +        SecDataDirLeft < WinCertificate->dwLength) {
>>>>        break;
>>>>      }
>>>>
>>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
>>>>      }
>>>>    }
>>>>
>>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
>>>> +  if (OffSet != SecDataDirEnd) {
>>>>      //
>>>>      // The Size in Certificate Table or the attribute certificate table is corrupted.
>>>>      //
>>>> --
>>>> 2.19.1.3.g30247aa5d201
>>>>
>>>
>>> Patch#2:
>>>
>>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep 17 00:00:00
>> 2001
>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
>>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib: assign
>>>>  WinCertificate after size check
>>>>
>>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) check only
>>>> guards the de-referencing of the "WinCertificate" pointer. It does not
>>>> guard the calculation of hte pointer itself:
>>>>
>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>
>>>> This is wrong; if we don't know for sure that we have enough room for a
>>>> WIN_CERTIFICATE, then even creating such a pointer, not just
>>>> de-referencing it, may invoke undefined behavior.
>>>>
>>>> Move the pointer calculation after the size check.
>>>>
>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>> ---
>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c | 8
>> +++++---
>>>>  1 file changed, 5 insertions(+), 3 deletions(-)
>>>>
>>>> diff --git
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> index 8761980c88aa..461ed7cfb5ac 100644
>>>> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> +++
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>         OffSet < SecDataDirEnd;
>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>> dwLength))) {
>>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
>>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>> -        SecDataDirLeft < WinCertificate->dwLength) {
>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
>>>> +      break;
>>>> +    }
>>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>        break;
>>>>      }
>>>>
>>>> --
>>>> 2.19.1.3.g30247aa5d201
>>>>
>>>
>>> Patch#3:
>>>
>>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17 00:00:00
>> 2001
>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
>>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch alignment
>>>>  overflow (CVE-2019-14562)
>>>>
>>>> The DxeImageVerificationHandler() function currently checks whether
>>>> "SecDataDir" has enough room for "WinCertificate->dwLength". However,
>> for
>>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to the next
>>>> multiple of 8. If "WinCertificate->dwLength" is large enough, the
>>>> alignment will return 0, and "OffSet" will be stuck at the same value.
>>>>
>>>> Check whether "SecDataDir" has room left for both
>>>> "WinCertificate->dwLength" and the alignment.
>>>>
>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>> ---
>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c | 4
>> +++-
>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> index 461ed7cfb5ac..e38eb981b7a0 100644
>>>> --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> +++
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
>>>>        break;
>>>>      }
>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
>>>> +        (SecDataDirLeft - WinCertificate->dwLength <
>>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
>>>>        break;
>>>>      }
>>>>
>>>> --
>>>> 2.19.1.3.g30247aa5d201
>>>>
>>>
>>> If Wenyi and the reviewers are OK with these patches, I can submit them
>>> as a standalone patch series.
>>>
>>> Note that I do not have any reproducer for the issue; the best testing
>>> that I could offer would be some light-weight Secure Boot regression
>>> tests.
>>>
>>> Thanks
>>> Laszlo
>>>
>>>
>>> .
>>>
>>
>>
>> 
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-17 16:52         ` Laszlo Ersek
@ 2020-08-17 23:23           ` Yao, Jiewen
  2020-08-18 10:17             ` Laszlo Ersek
  2020-08-18  2:10           ` Wang, Jian J
  1 sibling, 1 reply; 32+ messages in thread
From: Yao, Jiewen @ 2020-08-17 23:23 UTC (permalink / raw)
  To: devel@edk2.groups.io, lersek@redhat.com, xiewenyi2@huawei.com,
	Wang, Jian J
  Cc: huangming23@huawei.com, songdongkuang@huawei.com

Thanks Laszlo. Good feedback.
I think we need clarify the role and responsibility for the activity.

I provide my understanding and thought, and I would like to have more feedback from other people.


> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Laszlo Ersek
> Sent: Tuesday, August 18, 2020 12:53 AM
> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
> Cc: huangming23@huawei.com; songdongkuang@huawei.com
> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> 
> Hi Jiewen,
> 
> On 08/14/20 10:53, Yao, Jiewen wrote:
> >> To Jiewen,
> >> Sorry, I don't have environment to reproduce the issue.
> >
> > Please help me understand, if you don’t have environment to reproduce the
> issue, how do you guarantee that your patch does fix the problem and we don’t
> have any other vulnerabilities?
> 
> The original bug report in
> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is seriously
> lacking. It does not go into detail about the alleged integer overflow.
> It does not quote the code, does not explain the control flow, does not
> identify the exact edk2 commit at which the vulnerability exists.

[Jiewen] Yes, I agree with you that this is problematic.

> 
> The bug report also does not offer a reproducer.

[Jiewen] I don’t believe it is reporter's responsibility to provide a reproducer.
The reporter may provide a reproducer to convince other people the issue is there.
It is encouraged and highly recommended, but it is NOT mandatory.
Many times, people just review the code and happen to find something suspicious.
People can report the suspicious and ask for help on investigation. We should encourage that activity.

However, I do think the producer is mandatory for a fix or at least a security fix.
The owner to fix the issue should guarantee the patch is good.
The owner shall never rely on the code reviewer to figure out if the patch is good and complete.

I have some bad experience that bug owner just wrote a patch and tried to fix a problem, without any test.
And it happened passed code review from someone who does not well understand the problem, but give rb based upon the time pressure.
Later, the fix was approved to be useless.

In my memory, at least 3 cases were security fix. They are found, just because they are sensitive, more people took a look later.
    It was simple. It was one-line change.
   But it has not test, and it was wrong.
"It was ridiculous" -- commented by the people who find the so-called security fix does not fix the issue.


> 
> Additionally, the exact statement that the bug report does make, namely
> 
>   it's possible to overflow Offset back to 0 causing an endless loop
> 
> is wrong (as far as I can tell anyway). It is not "OffSet" that can be
> overflowed to zero, but the *addend* that is added to OffSet can be
> overflowed to zero. Therefore the infinite loop will arise because
> OffSet remains stuck at its present value, and not because OffSet will
> be re-set to zero.
> 
> For the reasons, we can only speculate as to what the actual problem is,
> unless Jian decides to join the discussion and clarifies what he had in
> mind originally.

[Jiewen] Would you please clarify what do you mean "we" here?
If "we" means the bug dispatcher, it is totally OK. The dispatcher just assign the bug.
If "we" means the developer assigned to fix the bug, it is NOT OK. The developer should take the responsibility to understand the problem. 

> 
> My understanding (or even "reconstruction") of the vulnerability is
> described above, and in the patches that I proposed.

[Jiewen] Yes. I tend to agree with you on the analysis. Appreciate your help!

> 
> We can write a patch based on code analysis. It's possible to identify
> integer overflows based on code analysis, and it's possible to verify
> the correctness of fixes by code review. Obviously testing is always
> good, but many times, constructing reproducers for such issues that were
> found by code review, is difficult and time consuming. We can say that
> we don't fix vulnerabilities without reproducers, or we can say that we
> make an effort to fix them even if all we have is code analysis (and not
> a reproducer).

[Jiewen] I would say: yes and no.
Yes, I agree with you that it might be difficult and time consuming to construct the reproducer.
However, "obviously" is a subject term. Someone may think something is obvious, but other people does not.
We should be clear the responsibility of the patch provider is to provide high quality patch.
Having basic unit test is the best way to prove that the fix is good.

I have seen bad cases when I ask for the test for patch, then the answer I got is: "I test the windows boot".
But the test - windows boot - has nothing related to the patch. It only proves no regression, but cannot prove the issue described is resolved.

Let's think again in this case, if the patch provider does some basic unit test, he/she may find out the problem by himself/herself.
That can save other people's time to review.

I don’t prefer to move the responsibility from patch provider to the code reviewer to check if the fix is good.
Otherwise, the code reviewer may be overwhelmed.

We may clarify and document the role and responsibility in EDKII clearly. Once that is ready, we can follow the rule.
Before that is ready, in this particular case, I still prefer we have producer to prove the patch is good.


> 
> So the above paragraph concerns "correctness". Regarding "completeness",
> I guarantee you that this patch does not fix *all* problems related to
> PE parsing. (See the other BZ tickets.) It does fix *one* issue with PE
> parsing. We can say that we try to fix such issues gradually (give
> different CVE numbers to different issues, and address them one at a
> time), or we can say that we rewrite PE parsing from the ground up.
> (BTW: I have seriously attempted that in the past, and I gave up,
> because the PE format is FUBAR.)

[Jiewen] Maybe there is misunderstanding.
I do not mean to let patch provider to fix all issue in PE parsing.
Just like we cannot file one Bugzilla to fix all issue in EDKII - it is unfair.

What I mean is that the patch provider should guarantee the correctness and completeness of the issue in the bug report.

One faked bad example of correctness is:
    A bug report is file to say: the code has overflow class A.
    The factor is: the code has overflow class A at line X and line Y.
    The patch only modified some code at line X, but the overflow class A at line X still exists.

One faked bad example of completeness is:
    A bug report is file to say: the code has overflow class A.
    The factor is: the code has overflow class A at line X and line Y.
    The patch only fixed the overflow class A at line X but not line Y.

The patch provider should take responsibility to do that work seriously to find out issue in line X and line Y and fix them.
He/she may choose to just fix line X and line Y. Rewrite is whole module is NOT required.



> 
> In summary:
> 
> - the problem statement is unclear,
[Jiewen] Agree.

> 
> - it seems like there is indeed an integer overflow problem in the
> SecDataDir parsing loop, but it's uncertain whether the bug reporter had
> exactly that in mind
[Jiewen] Agree.

> 
> - PE parsing is guaranteed to have other vulnerabilities elsewhere in
> edk2, but I'm currently unaware of other such issues in
> DxeImageVerificationLib specifically
[Jiewen] Agree.

> 
> - even if there are other such problems (in DxeImageVerificationLib or
> elswehere), fixing this bug that we know about is likely worthwhile
[Jiewen] Agree.

> 
> - for many such bugs, constructing a reproducer is difficult and time
> consuming; code analysis, and *regression-testing* are frequently the
> only tools we have. That doesn't mean we should ignore this class of bugs.
[Jiewen] I agree with you above two sentence.
And I still believe the reproducer is required. 

> 
> (Fixing integer overflows retro-actively is more difficult than writing
> overflow-free code in the first place, but that ship has sailed; so we
> can only fight these bugs incrementally now, unless we can rewrite PE
> parsing with a new data structure from the ground up. Again I tried that
> and gave up, because the spec is not public, and what I did manage to
> learn about PE, showed that it was insanely over-engineered. I'm not
> saying that other binary / executable formats are better, of course.)
> 
> Please check out my patches (inlined elsewhere in this thread), and
> comment whether you'd like me to post them to the list as a standalone
> series.
[Jiewen] From code structure perspective, I agree with you on the analysis. Appreciate that.

If I can give some comment, I would think about the provide the fix in BasePeCoffLib.
The reason is that: The PE parsing may scattered in the EDKII. The BasePeCoffLib will be consumed as the root of parsing.
Just in case, there is other parsing logic. It may consume the BasePeCoffLib but not ImageVerificationLib.
Having fix in ImageVerificationLib does not help to resolve the problem in other parser.
If we can reject the bad image in BasePeCoffLib, it would be the best way.

But I might be myopia and overlook some problem.
As such, having reproducer is the best way to prove and give confidence to other people.


> 
> Jian: it wouldn't hurt if you commented as well.
> 
> Thanks
> Laszlo
> 
> >> -----Original Message-----
> >> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
> wenyi,xie
> >> via groups.io
> >> Sent: Friday, August 14, 2020 3:54 PM
> >> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io; Yao, Jiewen
> >> <jiewen.yao@intel.com>; Wang, Jian J <jian.j.wang@intel.com>
> >> Cc: huangming23@huawei.com; songdongkuang@huawei.com
> >> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> >>
> >> To Laszlo,
> >> Thank you for your detailed description, I agree with what you analyzed and
> I'm
> >> OK with your patches, it's
> >> correct and much simpler.
> >>
> >> To Jiewen,
> >> Sorry, I don't have environment to reproduce the issue.
> >>
> >> Thanks
> >> Wenyi
> >>
> >> On 2020/8/14 2:50, Laszlo Ersek wrote:
> >>> On 08/13/20 13:55, Wenyi Xie wrote:
> >>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
> >>>>
> >>>> There is an integer overflow vulnerability in DxeImageVerificationHandler
> >>>> function when parsing the PE files attribute certificate table. In cases
> >>>> where WinCertificate->dwLength is sufficiently large, it's possible to
> >>>> overflow Offset back to 0 causing an endless loop.
> >>>>
> >>>> Check offset inbetween VirtualAddress and VirtualAddress + Size.
> >>>> Using SafeintLib to do offset addition with result check.
> >>>>
> >>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
> >>>> Cc: Jian J Wang <jian.j.wang@intel.com>
> >>>> Cc: Laszlo Ersek <lersek@redhat.com>
> >>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
> >>>> ---
> >>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> |
> >> 1 +
> >>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> |
> >> 1 +
> >>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> |
> >> 111 +++++++++++---------
> >>>>  3 files changed, 63 insertions(+), 50 deletions(-)
> >>>>
> >>>> diff --git
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> >>>> index 1e1a639857e0..a7ac4830b3d4 100644
> >>>> ---
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> >>>> +++
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> >>>> @@ -53,6 +53,7 @@ [LibraryClasses]
> >>>>    SecurityManagementLib
> >>>>    PeCoffLib
> >>>>    TpmMeasurementLib
> >>>> +  SafeIntLib
> >>>>
> >>>>  [Protocols]
> >>>>    gEfiFirmwareVolume2ProtocolGuid       ## SOMETIMES_CONSUMES
> >>>> diff --git
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> >>>> index 17955ff9774c..060273917d5d 100644
> >>>> ---
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> >>>> +++
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> >>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
> >>>>  #include <Library/DevicePathLib.h>
> >>>>  #include <Library/SecurityManagementLib.h>
> >>>>  #include <Library/PeCoffLib.h>
> >>>> +#include <Library/SafeIntLib.h>
> >>>>  #include <Protocol/FirmwareVolume2.h>
> >>>>  #include <Protocol/DevicePath.h>
> >>>>  #include <Protocol/BlockIo.h>
> >>>> diff --git
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> index 36b87e16d53d..dbc03e28c05b 100644
> >>>> ---
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> +++
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
> >>>>    EFI_STATUS                           HashStatus;
> >>>>    EFI_STATUS                           DbStatus;
> >>>>    BOOLEAN                              IsFound;
> >>>> +  UINT32                               AlignedLength;
> >>>> +  UINT32                               Result;
> >>>> +  EFI_STATUS                           AddStatus;
> >>>> +  BOOLEAN                              IsAuthDataAssigned;
> >>>>
> >>>>    SignatureList     = NULL;
> >>>>    SignatureListSize = 0;
> >>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
> >>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
> >>>>    IsVerified        = FALSE;
> >>>>    IsFound           = FALSE;
> >>>> +  Result            = 0;
> >>>>
> >>>>    //
> >>>>    // Check the image type and get policy setting.
> >>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
> >>>>    // The first certificate starts at offset (SecDataDir->VirtualAddress) from
> the
> >> start of the file.
> >>>>    //
> >>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >>> dwLength))) {
> >>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet < (SecDataDir-
> >>> VirtualAddress + SecDataDir->Size));) {
> >>>> +    IsAuthDataAssigned = FALSE;
> >>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE
> (WinCertificate-
> >>> dwLength);
> >>>
> >>> I disagree with this patch.
> >>>
> >>> The primary reason for my disagreement is that the bug report
> >>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is inexact, and
> >>> so this patch tries to fix the wrong thing.
> >>>
> >>> With edk2 master at commit 65904cdbb33c, it is *not* possible to
> >>> overflow the OffSet variable to zero with "WinCertificate->dwLength"
> >>> *purely*, and cause an endless loop. Note that we have (at commit
> >>> 65904cdbb33c):
> >>>
> >>>   for (OffSet = SecDataDir->VirtualAddress;
> >>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >>> dwLength))) {
> >>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
> >> (WIN_CERTIFICATE) ||
> >>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> WinCertificate-
> >>> dwLength) {
> >>>       break;
> >>>     }
> >>>
> >>> The last sub-condition checks whether the Security Data Directory has
> >>> enough room left for "WinCertificate->dwLength". If not, then we break
> >>> out of the loop.
> >>>
> >>> If we *do* have enough room, that is:
> >>>
> >>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >=
> WinCertificate-
> >>> dwLength
> >>>
> >>> then we have (by adding OffSet to both sides):
> >>>
> >>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet + WinCertificate-
> >>> dwLength
> >>>
> >>> The left hand side is a known-good UINT32, and so incrementing OffSet (a
> >>> UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32) does not
> >>> cause an overflow.
> >>>
> >>> Instead, the problem is with the alignment. The "if" statement checks
> >>> whether we have enough room for "dwLength", but then "OffSet" is
> >>> advanced by "dwLength" *aligned up* to the next multiple of 8. And that
> >>> may indeed cause various overflows.
> >>>
> >>> Now, the main problem with the present patch is that it does not fix one
> >>> of those overflows. Namely, consider that "dwLength" is very close to
> >>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then aligning it up
> >>> to the next multiple of 8 will yield 0. In other words, "AlignedLength"
> >>> will be zero.
> >>>
> >>> And when that happens, there's going to be an infinite loop just the
> >>> same: "OffSet" will not be zero, but it will be *stuck*. The
> >>> SafeUint32Add() call at the bottom will succeed, but it will not change
> >>> the value of "OffSet".
> >>>
> >>> More at the bottom.
> >>>
> >>>
> >>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
> >> (WIN_CERTIFICATE) ||
> >>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> >> WinCertificate->dwLength) {
> >>>>        break;
> >>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
> >>>>        }
> >>>>        AuthData   = PkcsCertData->CertData;
> >>>>        AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData-
> >Hdr);
> >>>> +      IsAuthDataAssigned = TRUE;
> >>>> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> >>>>      } else if (WinCertificate->wCertificateType ==
> WIN_CERT_TYPE_EFI_GUID)
> >> {
> >>>>        //
> >>>>        // The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which
> is
> >> described in UEFI Spec.
> >>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
> >>>>        if (WinCertUefiGuid->Hdr.dwLength <=
> >> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
> >>>>          break;
> >>>>        }
> >>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid))
> {
> >>>> -        continue;
> >>>> +      if (CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid))
> {
> >>>> +        AuthData = WinCertUefiGuid->CertData;
> >>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
> >> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
> >>>> +        IsAuthDataAssigned = TRUE;
> >>>> +        HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> >>>>        }
> >>>> -      AuthData = WinCertUefiGuid->CertData;
> >>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
> >> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
> >>>>      } else {
> >>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
> >>>>          break;
> >>>>        }
> >>>> -      continue;
> >>>>      }
> >>>>
> >>>> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> >>>> -    if (EFI_ERROR (HashStatus)) {
> >>>> -      continue;
> >>>> -    }
> >>>> -
> >>>> -    //
> >>>> -    // Check the digital signature against the revoked certificate in
> forbidden
> >> database (dbx).
> >>>> -    //
> >>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> >>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> >>>> -      IsVerified = FALSE;
> >>>> -      break;
> >>>> -    }
> >>>> -
> >>>> -    //
> >>>> -    // Check the digital signature against the valid certificate in allowed
> >> database (db).
> >>>> -    //
> >>>> -    if (!IsVerified) {
> >>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
> >>>> -        IsVerified = TRUE;
> >>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
> >>>> +      //
> >>>> +      // Check the digital signature against the revoked certificate in
> forbidden
> >> database (dbx).
> >>>> +      //
> >>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> >>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> >>>> +        IsVerified = FALSE;
> >>>> +        break;
> >>>>        }
> >>>> -    }
> >>>>
> >>>> -    //
> >>>> -    // Check the image's hash value.
> >>>> -    //
> >>>> -    DbStatus = IsSignatureFoundInDatabase (
> >>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
> >>>> -                 mImageDigest,
> >>>> -                 &mCertType,
> >>>> -                 mImageDigestSize,
> >>>> -                 &IsFound
> >>>> -                 );
> >>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
> >>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> >>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
> but %s
> >> hash of image is found in DBX.\n", mHashTypeStr));
> >>>> -      IsVerified = FALSE;
> >>>> -      break;
> >>>> -    }
> >>>> +      //
> >>>> +      // Check the digital signature against the valid certificate in allowed
> >> database (db).
> >>>> +      //
> >>>> +      if (!IsVerified) {
> >>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
> >>>> +          IsVerified = TRUE;
> >>>> +        }
> >>>> +      }
> >>>>
> >>>> -    if (!IsVerified) {
> >>>> +      //
> >>>> +      // Check the image's hash value.
> >>>> +      //
> >>>>        DbStatus = IsSignatureFoundInDatabase (
> >>>> -                   EFI_IMAGE_SECURITY_DATABASE,
> >>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
> >>>>                     mImageDigest,
> >>>>                     &mCertType,
> >>>>                     mImageDigestSize,
> >>>>                     &IsFound
> >>>>                     );
> >>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
> >>>> -        IsVerified = TRUE;
> >>>> -      } else {
> >>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
> but
> >> signature is not allowed by DB and %s hash of image is not found in
> DB/DBX.\n",
> >> mHashTypeStr));
> >>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
> >>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> >>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
> >> but %s hash of image is found in DBX.\n", mHashTypeStr));
> >>>> +        IsVerified = FALSE;
> >>>> +        break;
> >>>>        }
> >>>> +
> >>>> +      if (!IsVerified) {
> >>>> +        DbStatus = IsSignatureFoundInDatabase (
> >>>> +                     EFI_IMAGE_SECURITY_DATABASE,
> >>>> +                     mImageDigest,
> >>>> +                     &mCertType,
> >>>> +                     mImageDigestSize,
> >>>> +                     &IsFound
> >>>> +                     );
> >>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
> >>>> +          IsVerified = TRUE;
> >>>> +        } else {
> >>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
> but
> >> signature is not allowed by DB and %s hash of image is not found in
> DB/DBX.\n",
> >> mHashTypeStr));
> >>>> +        }
> >>>> +      }
> >>>> +    }
> >>>> +
> >>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
> >>>> +    if (EFI_ERROR (AddStatus)) {
> >>>> +      break;
> >>>>      }
> >>>> +    OffSet = Result;
> >>>>    }
> >>>>
> >>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
> >>>>
> >>>
> >>> There are other (smaller) reasons why I dislike this patch:
> >>>
> >>> - The "IsAuthDataAssigned" variable is superfluous; we could use the
> >>> existent "AuthData" variable (with a NULL-check and a NULL-assignment)
> >>> similarly.
> >>>
> >>> - The patch complicates / reorganizes the control flow needlessly. This
> >>> complication originates from placing the checked "OffSet" increment at
> >>> the bottom of the loop, which then requires the removal of all the
> >>> "continue" statements. But we don't need to check-and-increment at the
> >>> bottom. We can keep the increment inside the "for" statement, only
> >>> extend the *existent* room check (which I've quoted) to take the
> >>> alignment into account as well. If there is enough room for the
> >>> alignment in the security data directory, then that guarantees there
> >>> won't be a UINT32 overflow either.
> >>>
> >>> All in all, I'm proposing the following three patches instead. The first
> >>> two patches are preparation, the last patch is the fix.
> >>>
> >>> Patch#1:
> >>>
> >>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep 17
> 00:00:00
> >> 2001
> >>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
> >>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib: extract
> >>>>  SecDataDirEnd, SecDataDirLeft
> >>>>
> >>>> The following two quantities:
> >>>>
> >>>>   SecDataDir->VirtualAddress + SecDataDir->Size
> >>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
> >>>>
> >>>> are used multiple times in DxeImageVerificationHandler(). Introduce helper
> >>>> variables for them: "SecDataDirEnd" and "SecDataDirLeft", respectively.
> >>>> This saves us multiple calculations and significantly simplifies the code.
> >>>>
> >>>> Note that all three summands above have type UINT32, therefore the new
> >>>> variables are also of type UINT32.
> >>>>
> >>>> This patch does not change behavior.
> >>>>
> >>>> (Note that the code already handles the case when the
> >>>>
> >>>>   SecDataDir->VirtualAddress + SecDataDir->Size
> >>>>
> >>>> UINT32 addition overflows -- namely, in that case, the certificate loop is
> >>>> never entered, and the corruption check right after the loop fires.)
> >>>>
> >>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>> ---
> >>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c |
> 12
> >> ++++++++----
> >>>>  1 file changed, 8 insertions(+), 4 deletions(-)
> >>>>
> >>>> diff --git
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> index 36b87e16d53d..8761980c88aa 100644
> >>>> ---
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> +++
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
> >>>>    UINT8                                *AuthData;
> >>>>    UINTN                                AuthDataSize;
> >>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
> >>>> +  UINT32                               SecDataDirEnd;
> >>>> +  UINT32                               SecDataDirLeft;
> >>>>    UINT32                               OffSet;
> >>>>    CHAR16                               *NameStr;
> >>>>    RETURN_STATUS                        PeCoffStatus;
> >>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
> >>>>    // "Attribute Certificate Table".
> >>>>    // The first certificate starts at offset (SecDataDir->VirtualAddress) from
> the
> >> start of the file.
> >>>>    //
> >>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
> >>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>> +       OffSet < SecDataDirEnd;
> >>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >>> dwLength))) {
> >>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
> >> (WIN_CERTIFICATE) ||
> >>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> >> WinCertificate->dwLength) {
> >>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
> >>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
> >>>> +        SecDataDirLeft < WinCertificate->dwLength) {
> >>>>        break;
> >>>>      }
> >>>>
> >>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
> >>>>      }
> >>>>    }
> >>>>
> >>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
> >>>> +  if (OffSet != SecDataDirEnd) {
> >>>>      //
> >>>>      // The Size in Certificate Table or the attribute certificate table is
> corrupted.
> >>>>      //
> >>>> --
> >>>> 2.19.1.3.g30247aa5d201
> >>>>
> >>>
> >>> Patch#2:
> >>>
> >>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep 17 00:00:00
> >> 2001
> >>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
> >>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib: assign
> >>>>  WinCertificate after size check
> >>>>
> >>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) check only
> >>>> guards the de-referencing of the "WinCertificate" pointer. It does not
> >>>> guard the calculation of hte pointer itself:
> >>>>
> >>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>
> >>>> This is wrong; if we don't know for sure that we have enough room for a
> >>>> WIN_CERTIFICATE, then even creating such a pointer, not just
> >>>> de-referencing it, may invoke undefined behavior.
> >>>>
> >>>> Move the pointer calculation after the size check.
> >>>>
> >>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>> ---
> >>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c |
> 8
> >> +++++---
> >>>>  1 file changed, 5 insertions(+), 3 deletions(-)
> >>>>
> >>>> diff --git
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> index 8761980c88aa..461ed7cfb5ac 100644
> >>>> ---
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> +++
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
> >>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>>         OffSet < SecDataDirEnd;
> >>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >>> dwLength))) {
> >>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
> >>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
> >>>> -        SecDataDirLeft < WinCertificate->dwLength) {
> >>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
> >>>> +      break;
> >>>> +    }
> >>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
> >>>>        break;
> >>>>      }
> >>>>
> >>>> --
> >>>> 2.19.1.3.g30247aa5d201
> >>>>
> >>>
> >>> Patch#3:
> >>>
> >>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17 00:00:00
> >> 2001
> >>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
> >>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch
> alignment
> >>>>  overflow (CVE-2019-14562)
> >>>>
> >>>> The DxeImageVerificationHandler() function currently checks whether
> >>>> "SecDataDir" has enough room for "WinCertificate->dwLength". However,
> >> for
> >>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to the next
> >>>> multiple of 8. If "WinCertificate->dwLength" is large enough, the
> >>>> alignment will return 0, and "OffSet" will be stuck at the same value.
> >>>>
> >>>> Check whether "SecDataDir" has room left for both
> >>>> "WinCertificate->dwLength" and the alignment.
> >>>>
> >>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>> ---
> >>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c |
> 4
> >> +++-
> >>>>  1 file changed, 3 insertions(+), 1 deletion(-)
> >>>>
> >>>> diff --git
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> index 461ed7cfb5ac..e38eb981b7a0 100644
> >>>> ---
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> +++
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
> >>>>        break;
> >>>>      }
> >>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
> >>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
> >>>> +        (SecDataDirLeft - WinCertificate->dwLength <
> >>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
> >>>>        break;
> >>>>      }
> >>>>
> >>>> --
> >>>> 2.19.1.3.g30247aa5d201
> >>>>
> >>>
> >>> If Wenyi and the reviewers are OK with these patches, I can submit them
> >>> as a standalone patch series.
> >>>
> >>> Note that I do not have any reproducer for the issue; the best testing
> >>> that I could offer would be some light-weight Secure Boot regression
> >>> tests.
> >>>
> >>> Thanks
> >>> Laszlo
> >>>
> >>>
> >>> .
> >>>
> >>
> >>
> >>
> >
> 
> 
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-17 16:52         ` Laszlo Ersek
  2020-08-17 23:23           ` Yao, Jiewen
@ 2020-08-18  2:10           ` Wang, Jian J
  2020-08-18  8:58             ` Laszlo Ersek
  1 sibling, 1 reply; 32+ messages in thread
From: Wang, Jian J @ 2020-08-18  2:10 UTC (permalink / raw)
  To: devel@edk2.groups.io, lersek@redhat.com, Yao, Jiewen,
	xiewenyi2@huawei.com
  Cc: huangming23@huawei.com, songdongkuang@huawei.com, Mathews, John

Laszlo,

My apologies for the slow response. I'm not the original reporter but just the BZ
submitter. And I didn't do deep analysis to this issue. The issues was reported from
one internal team. Add John in loop to see if he knows more about it or not.

My superficial understanding on such issue is that, if there's "potential" issue in
theory and hard to reproduce, it's still worthy of using an alternative way to replace
the original implementation with no "potential" issue at all. Maybe we don't have
to prove old way is something wrong but must prove that the new way is really safe.

Regards,
Jian

> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Laszlo Ersek
> Sent: Tuesday, August 18, 2020 12:53 AM
> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
> Cc: huangming23@huawei.com; songdongkuang@huawei.com
> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> 
> Hi Jiewen,
> 
> On 08/14/20 10:53, Yao, Jiewen wrote:
> >> To Jiewen,
> >> Sorry, I don't have environment to reproduce the issue.
> >
> > Please help me understand, if you don’t have environment to reproduce the
> issue, how do you guarantee that your patch does fix the problem and we don’t
> have any other vulnerabilities?
> 
> The original bug report in
> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is seriously
> lacking. It does not go into detail about the alleged integer overflow.
> It does not quote the code, does not explain the control flow, does not
> identify the exact edk2 commit at which the vulnerability exists.
> 
> The bug report also does not offer a reproducer.
> 
> Additionally, the exact statement that the bug report does make, namely
> 
>   it's possible to overflow Offset back to 0 causing an endless loop
> 
> is wrong (as far as I can tell anyway). It is not "OffSet" that can be
> overflowed to zero, but the *addend* that is added to OffSet can be
> overflowed to zero. Therefore the infinite loop will arise because
> OffSet remains stuck at its present value, and not because OffSet will
> be re-set to zero.
> 
> For the reasons, we can only speculate as to what the actual problem is,
> unless Jian decides to join the discussion and clarifies what he had in
> mind originally.
> 
> My understanding (or even "reconstruction") of the vulnerability is
> described above, and in the patches that I proposed.
> 
> We can write a patch based on code analysis. It's possible to identify
> integer overflows based on code analysis, and it's possible to verify
> the correctness of fixes by code review. Obviously testing is always
> good, but many times, constructing reproducers for such issues that were
> found by code review, is difficult and time consuming. We can say that
> we don't fix vulnerabilities without reproducers, or we can say that we
> make an effort to fix them even if all we have is code analysis (and not
> a reproducer).
> 
> So the above paragraph concerns "correctness". Regarding "completeness",
> I guarantee you that this patch does not fix *all* problems related to
> PE parsing. (See the other BZ tickets.) It does fix *one* issue with PE
> parsing. We can say that we try to fix such issues gradually (give
> different CVE numbers to different issues, and address them one at a
> time), or we can say that we rewrite PE parsing from the ground up.
> (BTW: I have seriously attempted that in the past, and I gave up,
> because the PE format is FUBAR.)
> 
> In summary:
> 
> - the problem statement is unclear,
> 
> - it seems like there is indeed an integer overflow problem in the
> SecDataDir parsing loop, but it's uncertain whether the bug reporter had
> exactly that in mind
> 
> - PE parsing is guaranteed to have other vulnerabilities elsewhere in
> edk2, but I'm currently unaware of other such issues in
> DxeImageVerificationLib specifically
> 
> - even if there are other such problems (in DxeImageVerificationLib or
> elswehere), fixing this bug that we know about is likely worthwhile
> 
> - for many such bugs, constructing a reproducer is difficult and time
> consuming; code analysis, and *regression-testing* are frequently the
> only tools we have. That doesn't mean we should ignore this class of bugs.
> 
> (Fixing integer overflows retro-actively is more difficult than writing
> overflow-free code in the first place, but that ship has sailed; so we
> can only fight these bugs incrementally now, unless we can rewrite PE
> parsing with a new data structure from the ground up. Again I tried that
> and gave up, because the spec is not public, and what I did manage to
> learn about PE, showed that it was insanely over-engineered. I'm not
> saying that other binary / executable formats are better, of course.)
> 
> Please check out my patches (inlined elsewhere in this thread), and
> comment whether you'd like me to post them to the list as a standalone
> series.
> 
> Jian: it wouldn't hurt if you commented as well.
> 
> Thanks
> Laszlo
> 
> >> -----Original Message-----
> >> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
> wenyi,xie
> >> via groups.io
> >> Sent: Friday, August 14, 2020 3:54 PM
> >> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io; Yao, Jiewen
> >> <jiewen.yao@intel.com>; Wang, Jian J <jian.j.wang@intel.com>
> >> Cc: huangming23@huawei.com; songdongkuang@huawei.com
> >> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> >>
> >> To Laszlo,
> >> Thank you for your detailed description, I agree with what you analyzed and
> I'm
> >> OK with your patches, it's
> >> correct and much simpler.
> >>
> >> To Jiewen,
> >> Sorry, I don't have environment to reproduce the issue.
> >>
> >> Thanks
> >> Wenyi
> >>
> >> On 2020/8/14 2:50, Laszlo Ersek wrote:
> >>> On 08/13/20 13:55, Wenyi Xie wrote:
> >>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
> >>>>
> >>>> There is an integer overflow vulnerability in DxeImageVerificationHandler
> >>>> function when parsing the PE files attribute certificate table. In cases
> >>>> where WinCertificate->dwLength is sufficiently large, it's possible to
> >>>> overflow Offset back to 0 causing an endless loop.
> >>>>
> >>>> Check offset inbetween VirtualAddress and VirtualAddress + Size.
> >>>> Using SafeintLib to do offset addition with result check.
> >>>>
> >>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
> >>>> Cc: Jian J Wang <jian.j.wang@intel.com>
> >>>> Cc: Laszlo Ersek <lersek@redhat.com>
> >>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
> >>>> ---
> >>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> |
> >> 1 +
> >>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> |
> >> 1 +
> >>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> |
> >> 111 +++++++++++---------
> >>>>  3 files changed, 63 insertions(+), 50 deletions(-)
> >>>>
> >>>> diff --git
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> >>>> index 1e1a639857e0..a7ac4830b3d4 100644
> >>>> ---
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> >>>> +++
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
> >>>> @@ -53,6 +53,7 @@ [LibraryClasses]
> >>>>    SecurityManagementLib
> >>>>    PeCoffLib
> >>>>    TpmMeasurementLib
> >>>> +  SafeIntLib
> >>>>
> >>>>  [Protocols]
> >>>>    gEfiFirmwareVolume2ProtocolGuid       ## SOMETIMES_CONSUMES
> >>>> diff --git
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> >>>> index 17955ff9774c..060273917d5d 100644
> >>>> ---
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> >>>> +++
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
> >>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
> >>>>  #include <Library/DevicePathLib.h>
> >>>>  #include <Library/SecurityManagementLib.h>
> >>>>  #include <Library/PeCoffLib.h>
> >>>> +#include <Library/SafeIntLib.h>
> >>>>  #include <Protocol/FirmwareVolume2.h>
> >>>>  #include <Protocol/DevicePath.h>
> >>>>  #include <Protocol/BlockIo.h>
> >>>> diff --git
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> index 36b87e16d53d..dbc03e28c05b 100644
> >>>> ---
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> +++
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
> >>>>    EFI_STATUS                           HashStatus;
> >>>>    EFI_STATUS                           DbStatus;
> >>>>    BOOLEAN                              IsFound;
> >>>> +  UINT32                               AlignedLength;
> >>>> +  UINT32                               Result;
> >>>> +  EFI_STATUS                           AddStatus;
> >>>> +  BOOLEAN                              IsAuthDataAssigned;
> >>>>
> >>>>    SignatureList     = NULL;
> >>>>    SignatureListSize = 0;
> >>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
> >>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
> >>>>    IsVerified        = FALSE;
> >>>>    IsFound           = FALSE;
> >>>> +  Result            = 0;
> >>>>
> >>>>    //
> >>>>    // Check the image type and get policy setting.
> >>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
> >>>>    // The first certificate starts at offset (SecDataDir->VirtualAddress) from
> the
> >> start of the file.
> >>>>    //
> >>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >>> dwLength))) {
> >>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet < (SecDataDir-
> >>> VirtualAddress + SecDataDir->Size));) {
> >>>> +    IsAuthDataAssigned = FALSE;
> >>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE
> (WinCertificate-
> >>> dwLength);
> >>>
> >>> I disagree with this patch.
> >>>
> >>> The primary reason for my disagreement is that the bug report
> >>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is inexact, and
> >>> so this patch tries to fix the wrong thing.
> >>>
> >>> With edk2 master at commit 65904cdbb33c, it is *not* possible to
> >>> overflow the OffSet variable to zero with "WinCertificate->dwLength"
> >>> *purely*, and cause an endless loop. Note that we have (at commit
> >>> 65904cdbb33c):
> >>>
> >>>   for (OffSet = SecDataDir->VirtualAddress;
> >>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >>> dwLength))) {
> >>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
> >> (WIN_CERTIFICATE) ||
> >>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> WinCertificate-
> >>> dwLength) {
> >>>       break;
> >>>     }
> >>>
> >>> The last sub-condition checks whether the Security Data Directory has
> >>> enough room left for "WinCertificate->dwLength". If not, then we break
> >>> out of the loop.
> >>>
> >>> If we *do* have enough room, that is:
> >>>
> >>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >=
> WinCertificate-
> >>> dwLength
> >>>
> >>> then we have (by adding OffSet to both sides):
> >>>
> >>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet + WinCertificate-
> >>> dwLength
> >>>
> >>> The left hand side is a known-good UINT32, and so incrementing OffSet (a
> >>> UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32) does not
> >>> cause an overflow.
> >>>
> >>> Instead, the problem is with the alignment. The "if" statement checks
> >>> whether we have enough room for "dwLength", but then "OffSet" is
> >>> advanced by "dwLength" *aligned up* to the next multiple of 8. And that
> >>> may indeed cause various overflows.
> >>>
> >>> Now, the main problem with the present patch is that it does not fix one
> >>> of those overflows. Namely, consider that "dwLength" is very close to
> >>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then aligning it up
> >>> to the next multiple of 8 will yield 0. In other words, "AlignedLength"
> >>> will be zero.
> >>>
> >>> And when that happens, there's going to be an infinite loop just the
> >>> same: "OffSet" will not be zero, but it will be *stuck*. The
> >>> SafeUint32Add() call at the bottom will succeed, but it will not change
> >>> the value of "OffSet".
> >>>
> >>> More at the bottom.
> >>>
> >>>
> >>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
> >> (WIN_CERTIFICATE) ||
> >>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> >> WinCertificate->dwLength) {
> >>>>        break;
> >>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
> >>>>        }
> >>>>        AuthData   = PkcsCertData->CertData;
> >>>>        AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData-
> >Hdr);
> >>>> +      IsAuthDataAssigned = TRUE;
> >>>> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> >>>>      } else if (WinCertificate->wCertificateType ==
> WIN_CERT_TYPE_EFI_GUID)
> >> {
> >>>>        //
> >>>>        // The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which
> is
> >> described in UEFI Spec.
> >>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
> >>>>        if (WinCertUefiGuid->Hdr.dwLength <=
> >> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
> >>>>          break;
> >>>>        }
> >>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid))
> {
> >>>> -        continue;
> >>>> +      if (CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid))
> {
> >>>> +        AuthData = WinCertUefiGuid->CertData;
> >>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
> >> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
> >>>> +        IsAuthDataAssigned = TRUE;
> >>>> +        HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> >>>>        }
> >>>> -      AuthData = WinCertUefiGuid->CertData;
> >>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
> >> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
> >>>>      } else {
> >>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
> >>>>          break;
> >>>>        }
> >>>> -      continue;
> >>>>      }
> >>>>
> >>>> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> >>>> -    if (EFI_ERROR (HashStatus)) {
> >>>> -      continue;
> >>>> -    }
> >>>> -
> >>>> -    //
> >>>> -    // Check the digital signature against the revoked certificate in
> forbidden
> >> database (dbx).
> >>>> -    //
> >>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> >>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> >>>> -      IsVerified = FALSE;
> >>>> -      break;
> >>>> -    }
> >>>> -
> >>>> -    //
> >>>> -    // Check the digital signature against the valid certificate in allowed
> >> database (db).
> >>>> -    //
> >>>> -    if (!IsVerified) {
> >>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
> >>>> -        IsVerified = TRUE;
> >>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
> >>>> +      //
> >>>> +      // Check the digital signature against the revoked certificate in
> forbidden
> >> database (dbx).
> >>>> +      //
> >>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> >>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> >>>> +        IsVerified = FALSE;
> >>>> +        break;
> >>>>        }
> >>>> -    }
> >>>>
> >>>> -    //
> >>>> -    // Check the image's hash value.
> >>>> -    //
> >>>> -    DbStatus = IsSignatureFoundInDatabase (
> >>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
> >>>> -                 mImageDigest,
> >>>> -                 &mCertType,
> >>>> -                 mImageDigestSize,
> >>>> -                 &IsFound
> >>>> -                 );
> >>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
> >>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> >>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
> but %s
> >> hash of image is found in DBX.\n", mHashTypeStr));
> >>>> -      IsVerified = FALSE;
> >>>> -      break;
> >>>> -    }
> >>>> +      //
> >>>> +      // Check the digital signature against the valid certificate in allowed
> >> database (db).
> >>>> +      //
> >>>> +      if (!IsVerified) {
> >>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
> >>>> +          IsVerified = TRUE;
> >>>> +        }
> >>>> +      }
> >>>>
> >>>> -    if (!IsVerified) {
> >>>> +      //
> >>>> +      // Check the image's hash value.
> >>>> +      //
> >>>>        DbStatus = IsSignatureFoundInDatabase (
> >>>> -                   EFI_IMAGE_SECURITY_DATABASE,
> >>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
> >>>>                     mImageDigest,
> >>>>                     &mCertType,
> >>>>                     mImageDigestSize,
> >>>>                     &IsFound
> >>>>                     );
> >>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
> >>>> -        IsVerified = TRUE;
> >>>> -      } else {
> >>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
> but
> >> signature is not allowed by DB and %s hash of image is not found in
> DB/DBX.\n",
> >> mHashTypeStr));
> >>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
> >>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> >>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
> >> but %s hash of image is found in DBX.\n", mHashTypeStr));
> >>>> +        IsVerified = FALSE;
> >>>> +        break;
> >>>>        }
> >>>> +
> >>>> +      if (!IsVerified) {
> >>>> +        DbStatus = IsSignatureFoundInDatabase (
> >>>> +                     EFI_IMAGE_SECURITY_DATABASE,
> >>>> +                     mImageDigest,
> >>>> +                     &mCertType,
> >>>> +                     mImageDigestSize,
> >>>> +                     &IsFound
> >>>> +                     );
> >>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
> >>>> +          IsVerified = TRUE;
> >>>> +        } else {
> >>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
> but
> >> signature is not allowed by DB and %s hash of image is not found in
> DB/DBX.\n",
> >> mHashTypeStr));
> >>>> +        }
> >>>> +      }
> >>>> +    }
> >>>> +
> >>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
> >>>> +    if (EFI_ERROR (AddStatus)) {
> >>>> +      break;
> >>>>      }
> >>>> +    OffSet = Result;
> >>>>    }
> >>>>
> >>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
> >>>>
> >>>
> >>> There are other (smaller) reasons why I dislike this patch:
> >>>
> >>> - The "IsAuthDataAssigned" variable is superfluous; we could use the
> >>> existent "AuthData" variable (with a NULL-check and a NULL-assignment)
> >>> similarly.
> >>>
> >>> - The patch complicates / reorganizes the control flow needlessly. This
> >>> complication originates from placing the checked "OffSet" increment at
> >>> the bottom of the loop, which then requires the removal of all the
> >>> "continue" statements. But we don't need to check-and-increment at the
> >>> bottom. We can keep the increment inside the "for" statement, only
> >>> extend the *existent* room check (which I've quoted) to take the
> >>> alignment into account as well. If there is enough room for the
> >>> alignment in the security data directory, then that guarantees there
> >>> won't be a UINT32 overflow either.
> >>>
> >>> All in all, I'm proposing the following three patches instead. The first
> >>> two patches are preparation, the last patch is the fix.
> >>>
> >>> Patch#1:
> >>>
> >>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep 17
> 00:00:00
> >> 2001
> >>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
> >>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib: extract
> >>>>  SecDataDirEnd, SecDataDirLeft
> >>>>
> >>>> The following two quantities:
> >>>>
> >>>>   SecDataDir->VirtualAddress + SecDataDir->Size
> >>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
> >>>>
> >>>> are used multiple times in DxeImageVerificationHandler(). Introduce helper
> >>>> variables for them: "SecDataDirEnd" and "SecDataDirLeft", respectively.
> >>>> This saves us multiple calculations and significantly simplifies the code.
> >>>>
> >>>> Note that all three summands above have type UINT32, therefore the new
> >>>> variables are also of type UINT32.
> >>>>
> >>>> This patch does not change behavior.
> >>>>
> >>>> (Note that the code already handles the case when the
> >>>>
> >>>>   SecDataDir->VirtualAddress + SecDataDir->Size
> >>>>
> >>>> UINT32 addition overflows -- namely, in that case, the certificate loop is
> >>>> never entered, and the corruption check right after the loop fires.)
> >>>>
> >>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>> ---
> >>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c |
> 12
> >> ++++++++----
> >>>>  1 file changed, 8 insertions(+), 4 deletions(-)
> >>>>
> >>>> diff --git
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> index 36b87e16d53d..8761980c88aa 100644
> >>>> ---
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> +++
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
> >>>>    UINT8                                *AuthData;
> >>>>    UINTN                                AuthDataSize;
> >>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
> >>>> +  UINT32                               SecDataDirEnd;
> >>>> +  UINT32                               SecDataDirLeft;
> >>>>    UINT32                               OffSet;
> >>>>    CHAR16                               *NameStr;
> >>>>    RETURN_STATUS                        PeCoffStatus;
> >>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
> >>>>    // "Attribute Certificate Table".
> >>>>    // The first certificate starts at offset (SecDataDir->VirtualAddress) from
> the
> >> start of the file.
> >>>>    //
> >>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
> >>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>> +       OffSet < SecDataDirEnd;
> >>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >>> dwLength))) {
> >>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
> >> (WIN_CERTIFICATE) ||
> >>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> >> WinCertificate->dwLength) {
> >>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
> >>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
> >>>> +        SecDataDirLeft < WinCertificate->dwLength) {
> >>>>        break;
> >>>>      }
> >>>>
> >>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
> >>>>      }
> >>>>    }
> >>>>
> >>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
> >>>> +  if (OffSet != SecDataDirEnd) {
> >>>>      //
> >>>>      // The Size in Certificate Table or the attribute certificate table is
> corrupted.
> >>>>      //
> >>>> --
> >>>> 2.19.1.3.g30247aa5d201
> >>>>
> >>>
> >>> Patch#2:
> >>>
> >>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep 17 00:00:00
> >> 2001
> >>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
> >>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib: assign
> >>>>  WinCertificate after size check
> >>>>
> >>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) check only
> >>>> guards the de-referencing of the "WinCertificate" pointer. It does not
> >>>> guard the calculation of hte pointer itself:
> >>>>
> >>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>
> >>>> This is wrong; if we don't know for sure that we have enough room for a
> >>>> WIN_CERTIFICATE, then even creating such a pointer, not just
> >>>> de-referencing it, may invoke undefined behavior.
> >>>>
> >>>> Move the pointer calculation after the size check.
> >>>>
> >>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>> ---
> >>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c |
> 8
> >> +++++---
> >>>>  1 file changed, 5 insertions(+), 3 deletions(-)
> >>>>
> >>>> diff --git
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> index 8761980c88aa..461ed7cfb5ac 100644
> >>>> ---
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> +++
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
> >>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>>         OffSet < SecDataDirEnd;
> >>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >>> dwLength))) {
> >>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
> >>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
> >>>> -        SecDataDirLeft < WinCertificate->dwLength) {
> >>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
> >>>> +      break;
> >>>> +    }
> >>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
> >>>>        break;
> >>>>      }
> >>>>
> >>>> --
> >>>> 2.19.1.3.g30247aa5d201
> >>>>
> >>>
> >>> Patch#3:
> >>>
> >>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17 00:00:00
> >> 2001
> >>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
> >>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch
> alignment
> >>>>  overflow (CVE-2019-14562)
> >>>>
> >>>> The DxeImageVerificationHandler() function currently checks whether
> >>>> "SecDataDir" has enough room for "WinCertificate->dwLength". However,
> >> for
> >>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to the next
> >>>> multiple of 8. If "WinCertificate->dwLength" is large enough, the
> >>>> alignment will return 0, and "OffSet" will be stuck at the same value.
> >>>>
> >>>> Check whether "SecDataDir" has room left for both
> >>>> "WinCertificate->dwLength" and the alignment.
> >>>>
> >>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>> ---
> >>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c |
> 4
> >> +++-
> >>>>  1 file changed, 3 insertions(+), 1 deletion(-)
> >>>>
> >>>> diff --git
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> index 461ed7cfb5ac..e38eb981b7a0 100644
> >>>> ---
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> +++
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
> >>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
> >>>>        break;
> >>>>      }
> >>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
> >>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
> >>>> +        (SecDataDirLeft - WinCertificate->dwLength <
> >>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
> >>>>        break;
> >>>>      }
> >>>>
> >>>> --
> >>>> 2.19.1.3.g30247aa5d201
> >>>>
> >>>
> >>> If Wenyi and the reviewers are OK with these patches, I can submit them
> >>> as a standalone patch series.
> >>>
> >>> Note that I do not have any reproducer for the issue; the best testing
> >>> that I could offer would be some light-weight Secure Boot regression
> >>> tests.
> >>>
> >>> Thanks
> >>> Laszlo
> >>>
> >>>
> >>> .
> >>>
> >>
> >>
> >>
> >
> 
> 
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-18  2:10           ` Wang, Jian J
@ 2020-08-18  8:58             ` Laszlo Ersek
  2020-08-18 15:18               ` john.mathews
  0 siblings, 1 reply; 32+ messages in thread
From: Laszlo Ersek @ 2020-08-18  8:58 UTC (permalink / raw)
  To: Wang, Jian J, devel@edk2.groups.io, Yao, Jiewen,
	xiewenyi2@huawei.com
  Cc: huangming23@huawei.com, songdongkuang@huawei.com, Mathews, John

On 08/18/20 04:10, Wang, Jian J wrote:
> Laszlo,
> 
> My apologies for the slow response. I'm not the original reporter but just the BZ
> submitter. And I didn't do deep analysis to this issue. The issues was reported from
> one internal team. Add John in loop to see if he knows more about it or not.
> 
> My superficial understanding on such issue is that, if there's "potential" issue in
> theory and hard to reproduce, it's still worthy of using an alternative way to replace
> the original implementation with no "potential" issue at all. Maybe we don't have
> to prove old way is something wrong but must prove that the new way is really safe.

I agree, thanks.

It would be nice to hear more from the internal team about the
originally reported (even if hard-to-trigger) issue.

Thanks!
Laszlo

> 
> Regards,
> Jian
> 
>> -----Original Message-----
>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Laszlo Ersek
>> Sent: Tuesday, August 18, 2020 12:53 AM
>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>
>> Hi Jiewen,
>>
>> On 08/14/20 10:53, Yao, Jiewen wrote:
>>>> To Jiewen,
>>>> Sorry, I don't have environment to reproduce the issue.
>>>
>>> Please help me understand, if you don’t have environment to reproduce the
>> issue, how do you guarantee that your patch does fix the problem and we don’t
>> have any other vulnerabilities?
>>
>> The original bug report in
>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is seriously
>> lacking. It does not go into detail about the alleged integer overflow.
>> It does not quote the code, does not explain the control flow, does not
>> identify the exact edk2 commit at which the vulnerability exists.
>>
>> The bug report also does not offer a reproducer.
>>
>> Additionally, the exact statement that the bug report does make, namely
>>
>>   it's possible to overflow Offset back to 0 causing an endless loop
>>
>> is wrong (as far as I can tell anyway). It is not "OffSet" that can be
>> overflowed to zero, but the *addend* that is added to OffSet can be
>> overflowed to zero. Therefore the infinite loop will arise because
>> OffSet remains stuck at its present value, and not because OffSet will
>> be re-set to zero.
>>
>> For the reasons, we can only speculate as to what the actual problem is,
>> unless Jian decides to join the discussion and clarifies what he had in
>> mind originally.
>>
>> My understanding (or even "reconstruction") of the vulnerability is
>> described above, and in the patches that I proposed.
>>
>> We can write a patch based on code analysis. It's possible to identify
>> integer overflows based on code analysis, and it's possible to verify
>> the correctness of fixes by code review. Obviously testing is always
>> good, but many times, constructing reproducers for such issues that were
>> found by code review, is difficult and time consuming. We can say that
>> we don't fix vulnerabilities without reproducers, or we can say that we
>> make an effort to fix them even if all we have is code analysis (and not
>> a reproducer).
>>
>> So the above paragraph concerns "correctness". Regarding "completeness",
>> I guarantee you that this patch does not fix *all* problems related to
>> PE parsing. (See the other BZ tickets.) It does fix *one* issue with PE
>> parsing. We can say that we try to fix such issues gradually (give
>> different CVE numbers to different issues, and address them one at a
>> time), or we can say that we rewrite PE parsing from the ground up.
>> (BTW: I have seriously attempted that in the past, and I gave up,
>> because the PE format is FUBAR.)
>>
>> In summary:
>>
>> - the problem statement is unclear,
>>
>> - it seems like there is indeed an integer overflow problem in the
>> SecDataDir parsing loop, but it's uncertain whether the bug reporter had
>> exactly that in mind
>>
>> - PE parsing is guaranteed to have other vulnerabilities elsewhere in
>> edk2, but I'm currently unaware of other such issues in
>> DxeImageVerificationLib specifically
>>
>> - even if there are other such problems (in DxeImageVerificationLib or
>> elswehere), fixing this bug that we know about is likely worthwhile
>>
>> - for many such bugs, constructing a reproducer is difficult and time
>> consuming; code analysis, and *regression-testing* are frequently the
>> only tools we have. That doesn't mean we should ignore this class of bugs.
>>
>> (Fixing integer overflows retro-actively is more difficult than writing
>> overflow-free code in the first place, but that ship has sailed; so we
>> can only fight these bugs incrementally now, unless we can rewrite PE
>> parsing with a new data structure from the ground up. Again I tried that
>> and gave up, because the spec is not public, and what I did manage to
>> learn about PE, showed that it was insanely over-engineered. I'm not
>> saying that other binary / executable formats are better, of course.)
>>
>> Please check out my patches (inlined elsewhere in this thread), and
>> comment whether you'd like me to post them to the list as a standalone
>> series.
>>
>> Jian: it wouldn't hurt if you commented as well.
>>
>> Thanks
>> Laszlo
>>
>>>> -----Original Message-----
>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>> wenyi,xie
>>>> via groups.io
>>>> Sent: Friday, August 14, 2020 3:54 PM
>>>> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io; Yao, Jiewen
>>>> <jiewen.yao@intel.com>; Wang, Jian J <jian.j.wang@intel.com>
>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>
>>>> To Laszlo,
>>>> Thank you for your detailed description, I agree with what you analyzed and
>> I'm
>>>> OK with your patches, it's
>>>> correct and much simpler.
>>>>
>>>> To Jiewen,
>>>> Sorry, I don't have environment to reproduce the issue.
>>>>
>>>> Thanks
>>>> Wenyi
>>>>
>>>> On 2020/8/14 2:50, Laszlo Ersek wrote:
>>>>> On 08/13/20 13:55, Wenyi Xie wrote:
>>>>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
>>>>>>
>>>>>> There is an integer overflow vulnerability in DxeImageVerificationHandler
>>>>>> function when parsing the PE files attribute certificate table. In cases
>>>>>> where WinCertificate->dwLength is sufficiently large, it's possible to
>>>>>> overflow Offset back to 0 causing an endless loop.
>>>>>>
>>>>>> Check offset inbetween VirtualAddress and VirtualAddress + Size.
>>>>>> Using SafeintLib to do offset addition with result check.
>>>>>>
>>>>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>>>>>> Cc: Jian J Wang <jian.j.wang@intel.com>
>>>>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>>>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
>>>>>> ---
>>>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
>> |
>>>> 1 +
>>>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
>> |
>>>> 1 +
>>>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>> |
>>>> 111 +++++++++++---------
>>>>>>  3 files changed, 63 insertions(+), 50 deletions(-)
>>>>>>
>>>>>> diff --git
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
>>>>>> index 1e1a639857e0..a7ac4830b3d4 100644
>>>>>> ---
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
>>>>>> +++
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
>>>>>> @@ -53,6 +53,7 @@ [LibraryClasses]
>>>>>>    SecurityManagementLib
>>>>>>    PeCoffLib
>>>>>>    TpmMeasurementLib
>>>>>> +  SafeIntLib
>>>>>>
>>>>>>  [Protocols]
>>>>>>    gEfiFirmwareVolume2ProtocolGuid       ## SOMETIMES_CONSUMES
>>>>>> diff --git
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
>>>>>> index 17955ff9774c..060273917d5d 100644
>>>>>> ---
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
>>>>>> +++
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h
>>>>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
>>>>>>  #include <Library/DevicePathLib.h>
>>>>>>  #include <Library/SecurityManagementLib.h>
>>>>>>  #include <Library/PeCoffLib.h>
>>>>>> +#include <Library/SafeIntLib.h>
>>>>>>  #include <Protocol/FirmwareVolume2.h>
>>>>>>  #include <Protocol/DevicePath.h>
>>>>>>  #include <Protocol/BlockIo.h>
>>>>>> diff --git
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>>>> index 36b87e16d53d..dbc03e28c05b 100644
>>>>>> ---
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>>>> +++
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
>>>>>>    EFI_STATUS                           HashStatus;
>>>>>>    EFI_STATUS                           DbStatus;
>>>>>>    BOOLEAN                              IsFound;
>>>>>> +  UINT32                               AlignedLength;
>>>>>> +  UINT32                               Result;
>>>>>> +  EFI_STATUS                           AddStatus;
>>>>>> +  BOOLEAN                              IsAuthDataAssigned;
>>>>>>
>>>>>>    SignatureList     = NULL;
>>>>>>    SignatureListSize = 0;
>>>>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
>>>>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
>>>>>>    IsVerified        = FALSE;
>>>>>>    IsFound           = FALSE;
>>>>>> +  Result            = 0;
>>>>>>
>>>>>>    //
>>>>>>    // Check the image type and get policy setting.
>>>>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
>>>>>>    // The first certificate starts at offset (SecDataDir->VirtualAddress) from
>> the
>>>> start of the file.
>>>>>>    //
>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>>>> dwLength))) {
>>>>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet < (SecDataDir-
>>>>> VirtualAddress + SecDataDir->Size));) {
>>>>>> +    IsAuthDataAssigned = FALSE;
>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE
>> (WinCertificate-
>>>>> dwLength);
>>>>>
>>>>> I disagree with this patch.
>>>>>
>>>>> The primary reason for my disagreement is that the bug report
>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is inexact, and
>>>>> so this patch tries to fix the wrong thing.
>>>>>
>>>>> With edk2 master at commit 65904cdbb33c, it is *not* possible to
>>>>> overflow the OffSet variable to zero with "WinCertificate->dwLength"
>>>>> *purely*, and cause an endless loop. Note that we have (at commit
>>>>> 65904cdbb33c):
>>>>>
>>>>>   for (OffSet = SecDataDir->VirtualAddress;
>>>>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>>>> dwLength))) {
>>>>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>>>> (WIN_CERTIFICATE) ||
>>>>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>> WinCertificate-
>>>>> dwLength) {
>>>>>       break;
>>>>>     }
>>>>>
>>>>> The last sub-condition checks whether the Security Data Directory has
>>>>> enough room left for "WinCertificate->dwLength". If not, then we break
>>>>> out of the loop.
>>>>>
>>>>> If we *do* have enough room, that is:
>>>>>
>>>>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >=
>> WinCertificate-
>>>>> dwLength
>>>>>
>>>>> then we have (by adding OffSet to both sides):
>>>>>
>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet + WinCertificate-
>>>>> dwLength
>>>>>
>>>>> The left hand side is a known-good UINT32, and so incrementing OffSet (a
>>>>> UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32) does not
>>>>> cause an overflow.
>>>>>
>>>>> Instead, the problem is with the alignment. The "if" statement checks
>>>>> whether we have enough room for "dwLength", but then "OffSet" is
>>>>> advanced by "dwLength" *aligned up* to the next multiple of 8. And that
>>>>> may indeed cause various overflows.
>>>>>
>>>>> Now, the main problem with the present patch is that it does not fix one
>>>>> of those overflows. Namely, consider that "dwLength" is very close to
>>>>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then aligning it up
>>>>> to the next multiple of 8 will yield 0. In other words, "AlignedLength"
>>>>> will be zero.
>>>>>
>>>>> And when that happens, there's going to be an infinite loop just the
>>>>> same: "OffSet" will not be zero, but it will be *stuck*. The
>>>>> SafeUint32Add() call at the bottom will succeed, but it will not change
>>>>> the value of "OffSet".
>>>>>
>>>>> More at the bottom.
>>>>>
>>>>>
>>>>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>>>> (WIN_CERTIFICATE) ||
>>>>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>> WinCertificate->dwLength) {
>>>>>>        break;
>>>>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
>>>>>>        }
>>>>>>        AuthData   = PkcsCertData->CertData;
>>>>>>        AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData-
>>> Hdr);
>>>>>> +      IsAuthDataAssigned = TRUE;
>>>>>> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>      } else if (WinCertificate->wCertificateType ==
>> WIN_CERT_TYPE_EFI_GUID)
>>>> {
>>>>>>        //
>>>>>>        // The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which
>> is
>>>> described in UEFI Spec.
>>>>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
>>>>>>        if (WinCertUefiGuid->Hdr.dwLength <=
>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
>>>>>>          break;
>>>>>>        }
>>>>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid))
>> {
>>>>>> -        continue;
>>>>>> +      if (CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid))
>> {
>>>>>> +        AuthData = WinCertUefiGuid->CertData;
>>>>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>> +        IsAuthDataAssigned = TRUE;
>>>>>> +        HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>        }
>>>>>> -      AuthData = WinCertUefiGuid->CertData;
>>>>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>      } else {
>>>>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
>>>>>>          break;
>>>>>>        }
>>>>>> -      continue;
>>>>>>      }
>>>>>>
>>>>>> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>> -    if (EFI_ERROR (HashStatus)) {
>>>>>> -      continue;
>>>>>> -    }
>>>>>> -
>>>>>> -    //
>>>>>> -    // Check the digital signature against the revoked certificate in
>> forbidden
>>>> database (dbx).
>>>>>> -    //
>>>>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>> -      IsVerified = FALSE;
>>>>>> -      break;
>>>>>> -    }
>>>>>> -
>>>>>> -    //
>>>>>> -    // Check the digital signature against the valid certificate in allowed
>>>> database (db).
>>>>>> -    //
>>>>>> -    if (!IsVerified) {
>>>>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>> -        IsVerified = TRUE;
>>>>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
>>>>>> +      //
>>>>>> +      // Check the digital signature against the revoked certificate in
>> forbidden
>>>> database (dbx).
>>>>>> +      //
>>>>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>> +        IsVerified = FALSE;
>>>>>> +        break;
>>>>>>        }
>>>>>> -    }
>>>>>>
>>>>>> -    //
>>>>>> -    // Check the image's hash value.
>>>>>> -    //
>>>>>> -    DbStatus = IsSignatureFoundInDatabase (
>>>>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
>>>>>> -                 mImageDigest,
>>>>>> -                 &mCertType,
>>>>>> -                 mImageDigestSize,
>>>>>> -                 &IsFound
>>>>>> -                 );
>>>>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
>> but %s
>>>> hash of image is found in DBX.\n", mHashTypeStr));
>>>>>> -      IsVerified = FALSE;
>>>>>> -      break;
>>>>>> -    }
>>>>>> +      //
>>>>>> +      // Check the digital signature against the valid certificate in allowed
>>>> database (db).
>>>>>> +      //
>>>>>> +      if (!IsVerified) {
>>>>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>> +          IsVerified = TRUE;
>>>>>> +        }
>>>>>> +      }
>>>>>>
>>>>>> -    if (!IsVerified) {
>>>>>> +      //
>>>>>> +      // Check the image's hash value.
>>>>>> +      //
>>>>>>        DbStatus = IsSignatureFoundInDatabase (
>>>>>> -                   EFI_IMAGE_SECURITY_DATABASE,
>>>>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>                     mImageDigest,
>>>>>>                     &mCertType,
>>>>>>                     mImageDigestSize,
>>>>>>                     &IsFound
>>>>>>                     );
>>>>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>> -        IsVerified = TRUE;
>>>>>> -      } else {
>>>>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
>> but
>>>> signature is not allowed by DB and %s hash of image is not found in
>> DB/DBX.\n",
>>>> mHashTypeStr));
>>>>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
>>>> but %s hash of image is found in DBX.\n", mHashTypeStr));
>>>>>> +        IsVerified = FALSE;
>>>>>> +        break;
>>>>>>        }
>>>>>> +
>>>>>> +      if (!IsVerified) {
>>>>>> +        DbStatus = IsSignatureFoundInDatabase (
>>>>>> +                     EFI_IMAGE_SECURITY_DATABASE,
>>>>>> +                     mImageDigest,
>>>>>> +                     &mCertType,
>>>>>> +                     mImageDigestSize,
>>>>>> +                     &IsFound
>>>>>> +                     );
>>>>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>> +          IsVerified = TRUE;
>>>>>> +        } else {
>>>>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
>> but
>>>> signature is not allowed by DB and %s hash of image is not found in
>> DB/DBX.\n",
>>>> mHashTypeStr));
>>>>>> +        }
>>>>>> +      }
>>>>>> +    }
>>>>>> +
>>>>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
>>>>>> +    if (EFI_ERROR (AddStatus)) {
>>>>>> +      break;
>>>>>>      }
>>>>>> +    OffSet = Result;
>>>>>>    }
>>>>>>
>>>>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
>>>>>>
>>>>>
>>>>> There are other (smaller) reasons why I dislike this patch:
>>>>>
>>>>> - The "IsAuthDataAssigned" variable is superfluous; we could use the
>>>>> existent "AuthData" variable (with a NULL-check and a NULL-assignment)
>>>>> similarly.
>>>>>
>>>>> - The patch complicates / reorganizes the control flow needlessly. This
>>>>> complication originates from placing the checked "OffSet" increment at
>>>>> the bottom of the loop, which then requires the removal of all the
>>>>> "continue" statements. But we don't need to check-and-increment at the
>>>>> bottom. We can keep the increment inside the "for" statement, only
>>>>> extend the *existent* room check (which I've quoted) to take the
>>>>> alignment into account as well. If there is enough room for the
>>>>> alignment in the security data directory, then that guarantees there
>>>>> won't be a UINT32 overflow either.
>>>>>
>>>>> All in all, I'm proposing the following three patches instead. The first
>>>>> two patches are preparation, the last patch is the fix.
>>>>>
>>>>> Patch#1:
>>>>>
>>>>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep 17
>> 00:00:00
>>>> 2001
>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
>>>>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib: extract
>>>>>>  SecDataDirEnd, SecDataDirLeft
>>>>>>
>>>>>> The following two quantities:
>>>>>>
>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
>>>>>>
>>>>>> are used multiple times in DxeImageVerificationHandler(). Introduce helper
>>>>>> variables for them: "SecDataDirEnd" and "SecDataDirLeft", respectively.
>>>>>> This saves us multiple calculations and significantly simplifies the code.
>>>>>>
>>>>>> Note that all three summands above have type UINT32, therefore the new
>>>>>> variables are also of type UINT32.
>>>>>>
>>>>>> This patch does not change behavior.
>>>>>>
>>>>>> (Note that the code already handles the case when the
>>>>>>
>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>
>>>>>> UINT32 addition overflows -- namely, in that case, the certificate loop is
>>>>>> never entered, and the corruption check right after the loop fires.)
>>>>>>
>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>> ---
>>>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c |
>> 12
>>>> ++++++++----
>>>>>>  1 file changed, 8 insertions(+), 4 deletions(-)
>>>>>>
>>>>>> diff --git
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>>>> index 36b87e16d53d..8761980c88aa 100644
>>>>>> ---
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>>>> +++
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
>>>>>>    UINT8                                *AuthData;
>>>>>>    UINTN                                AuthDataSize;
>>>>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
>>>>>> +  UINT32                               SecDataDirEnd;
>>>>>> +  UINT32                               SecDataDirLeft;
>>>>>>    UINT32                               OffSet;
>>>>>>    CHAR16                               *NameStr;
>>>>>>    RETURN_STATUS                        PeCoffStatus;
>>>>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
>>>>>>    // "Attribute Certificate Table".
>>>>>>    // The first certificate starts at offset (SecDataDir->VirtualAddress) from
>> the
>>>> start of the file.
>>>>>>    //
>>>>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>> +       OffSet < SecDataDirEnd;
>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>>>> dwLength))) {
>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>>>> (WIN_CERTIFICATE) ||
>>>>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>> WinCertificate->dwLength) {
>>>>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>> +        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>        break;
>>>>>>      }
>>>>>>
>>>>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
>>>>>>      }
>>>>>>    }
>>>>>>
>>>>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
>>>>>> +  if (OffSet != SecDataDirEnd) {
>>>>>>      //
>>>>>>      // The Size in Certificate Table or the attribute certificate table is
>> corrupted.
>>>>>>      //
>>>>>> --
>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>
>>>>>
>>>>> Patch#2:
>>>>>
>>>>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep 17 00:00:00
>>>> 2001
>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
>>>>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib: assign
>>>>>>  WinCertificate after size check
>>>>>>
>>>>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) check only
>>>>>> guards the de-referencing of the "WinCertificate" pointer. It does not
>>>>>> guard the calculation of hte pointer itself:
>>>>>>
>>>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>
>>>>>> This is wrong; if we don't know for sure that we have enough room for a
>>>>>> WIN_CERTIFICATE, then even creating such a pointer, not just
>>>>>> de-referencing it, may invoke undefined behavior.
>>>>>>
>>>>>> Move the pointer calculation after the size check.
>>>>>>
>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>> ---
>>>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c |
>> 8
>>>> +++++---
>>>>>>  1 file changed, 5 insertions(+), 3 deletions(-)
>>>>>>
>>>>>> diff --git
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>>>> index 8761980c88aa..461ed7cfb5ac 100644
>>>>>> ---
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>>>> +++
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>         OffSet < SecDataDirEnd;
>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>>>> dwLength))) {
>>>>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>> -        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
>>>>>> +      break;
>>>>>> +    }
>>>>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>        break;
>>>>>>      }
>>>>>>
>>>>>> --
>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>
>>>>>
>>>>> Patch#3:
>>>>>
>>>>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17 00:00:00
>>>> 2001
>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
>>>>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch
>> alignment
>>>>>>  overflow (CVE-2019-14562)
>>>>>>
>>>>>> The DxeImageVerificationHandler() function currently checks whether
>>>>>> "SecDataDir" has enough room for "WinCertificate->dwLength". However,
>>>> for
>>>>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to the next
>>>>>> multiple of 8. If "WinCertificate->dwLength" is large enough, the
>>>>>> alignment will return 0, and "OffSet" will be stuck at the same value.
>>>>>>
>>>>>> Check whether "SecDataDir" has room left for both
>>>>>> "WinCertificate->dwLength" and the alignment.
>>>>>>
>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>> ---
>>>>>>  SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c |
>> 4
>>>> +++-
>>>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>>>>
>>>>>> diff --git
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>>>> index 461ed7cfb5ac..e38eb981b7a0 100644
>>>>>> ---
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>>>> +++
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
>>>>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
>>>>>>        break;
>>>>>>      }
>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
>>>>>> +        (SecDataDirLeft - WinCertificate->dwLength <
>>>>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
>>>>>>        break;
>>>>>>      }
>>>>>>
>>>>>> --
>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>
>>>>>
>>>>> If Wenyi and the reviewers are OK with these patches, I can submit them
>>>>> as a standalone patch series.
>>>>>
>>>>> Note that I do not have any reproducer for the issue; the best testing
>>>>> that I could offer would be some light-weight Secure Boot regression
>>>>> tests.
>>>>>
>>>>> Thanks
>>>>> Laszlo
>>>>>
>>>>>
>>>>> .
>>>>>
>>>>
>>>>
>>>>
>>>
>>
>>
>> 
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-17 23:23           ` Yao, Jiewen
@ 2020-08-18 10:17             ` Laszlo Ersek
       [not found]               ` <a7elBrHZ3zD0Stt3MiPOUU_6uOnp-LlR4c9weDhWm4xYH388XWK0M80fLZe_AqbzF68IFK_IdkWQtKN8HKyRnQ==@protonmail.internalid>
                                 ` (2 more replies)
  0 siblings, 3 replies; 32+ messages in thread
From: Laszlo Ersek @ 2020-08-18 10:17 UTC (permalink / raw)
  To: Yao, Jiewen
  Cc: devel@edk2.groups.io, xiewenyi2@huawei.com, Wang, Jian J,
	huangming23@huawei.com, songdongkuang@huawei.com,
	Marvin Häuser, Vitaly Cheptsov

Hi Jiewen,

(+Marvin, +Vitaly)

On 08/18/20 01:23, Yao, Jiewen wrote:
>> -----Original Message-----
>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Laszlo
>> Ersek
>> Sent: Tuesday, August 18, 2020 12:53 AM
>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset

[...]

> However, I do think the producer is mandatory for a fix or at least a
> security fix.
> The owner to fix the issue should guarantee the patch is good.
> The owner shall never rely on the code reviewer to figure out if the
> patch is good and complete.
>
> I have some bad experience that bug owner just wrote a patch and tried
> to fix a problem, without any test.
> And it happened passed code review from someone who does not well
> understand the problem, but give rb based upon the time pressure.
> Later, the fix was approved to be useless.
>
> In my memory, at least 3 cases were security fix. They are found, just
> because they are sensitive, more people took a look later.
>     It was simple. It was one-line change.
>    But it has not test, and it was wrong.
> "It was ridiculous" -- commented by the people who find the so-called
> security fix does not fix the issue.

Just because sloppy/rushed reviews exist, and just because reviewers
operate under time pressure, we should not automatically reject security
fixes that come without a reproducer.

Some organizations do develop reproducers, but they never share them
publicly (for fear of abuse by others).

But more importantly, in an open development project, a developer could
have time and expertise to contribute a fix, but not to create a
reproducer.

- If we make contributing harder, fewer people will upstream their
  fixes.

- If we make contributing harder, then contributions that do make it to
  the tree will be of higher quality.

Both statements ring true to me -- so it's a tradeoff.

(By "we", I mean the edk2 community.)

>> Additionally, the exact statement that the bug report does make,
>> namely
>>
>>   it's possible to overflow Offset back to 0 causing an endless loop
>>
>> is wrong (as far as I can tell anyway). It is not "OffSet" that can
>> be overflowed to zero, but the *addend* that is added to OffSet can
>> be overflowed to zero. Therefore the infinite loop will arise because
>> OffSet remains stuck at its present value, and not because OffSet
>> will be re-set to zero.
>>
>> For the reasons, we can only speculate as to what the actual problem
>> is, unless Jian decides to join the discussion and clarifies what he
>> had in mind originally.
>
> [Jiewen] Would you please clarify what do you mean "we" here?
> If "we" means the bug dispatcher, it is totally OK. The dispatcher
> just assign the bug.
> If "we" means the developer assigned to fix the bug, it is NOT OK. The
> developer should take the responsibility to understand the problem.

By "we", I mean the edk2 community.

>> We can write a patch based on code analysis. It's possible to
>> identify integer overflows based on code analysis, and it's possible
>> to verify the correctness of fixes by code review. Obviously testing
>> is always good, but many times, constructing reproducers for such
>> issues that were found by code review, is difficult and time
>> consuming. We can say that we don't fix vulnerabilities without
>> reproducers, or we can say that we make an effort to fix them even if
>> all we have is code analysis (and not a reproducer).
>
> [Jiewen] I would say: yes and no.
> Yes, I agree with you that it might be difficult and time consuming to
> construct the reproducer.
> However, "obviously" is a subject term. Someone may think something is
> obvious, but other people does not.
> We should be clear the responsibility of the patch provider is to
> provide high quality patch.
> Having basic unit test is the best way to prove that the fix is good.
>
> I have seen bad cases when I ask for the test for patch, then the
> answer I got is: "I test the windows boot".
> But the test - windows boot - has nothing related to the patch. It
> only proves no regression, but cannot prove the issue described is
> resolved.

Right. It would be ideal if every patch came with a unit test. But that
also means some folks will contribute less.

Consider normal (not security) patches. We require that all function
return values be checked (unless it really doesn't matter if a function
call fails). If a function call fails, we need to roll back the actions
taken thus far. Release resources and so on. This is why we have the
"cascade of error handling labels" pattern.

But, of course, we don't test every possible error path in the code. So
what's the solution there:

- reject such patches that carefully construct the error paths, but do
  not provide unit tests with complete error path coverage?

- say that we don't care about thorough error paths, so let's just hang,
  or leak resources, whenever something fails?

Personally I prefer the detailed error paths. They need to be written
and reviewed carefully. And they can be accepted even if they are not
tested with complete coverage.

Some people think otherwise; they say no untested (untestable) code
should ever be merged.

Back to security patches -- creating reproducers usually requires a
setup (tools, expertise, time allocation etc) that is different from a
"normal" setup. It may require specialized binary format editors,
expertise in "penetration testing", and so on.

I mostly know the C language rules that pertain to integer and buffer
overflows, so I can usually spot their violations in C code, and propose
fixes for them too. But I'm not a security researcher, so I don't write
exploits as a norm -- I don't even investigate, generally speaking, the
potential practical impact of "undefined behavior". When there's a
buffer overflow or integer overflow in the code, that's the *end* of the
story for me, while it's the *start* of the work for a security
researcher.

When you require reproducers for all security patches, you restrict the
potential contributor pool to security researchers.

> Let's think again in this case, if the patch provider does some basic
> unit test, he/she may find out the problem by himself/herself.
> That can save other people's time to review.
>
> I don't prefer to move the responsibility from patch provider to the
> code reviewer to check if the fix is good.
> Otherwise, the code reviewer may be overwhelmed.
>
> We may clarify and document the role and responsibility in EDKII
> clearly. Once that is ready, we can follow the rule.
> Before that is ready, in this particular case, I still prefer we have
> producer to prove the patch is good.

OK, thanks for explaining.

Given that I'm unable to create such a PE file (from scratch or by
modifying another one), I won't post the patches stand-alone.

>> So the above paragraph concerns "correctness". Regarding
>> "completeness", I guarantee you that this patch does not fix *all*
>> problems related to PE parsing. (See the other BZ tickets.) It does
>> fix *one* issue with PE parsing. We can say that we try to fix such
>> issues gradually (give different CVE numbers to different issues, and
>> address them one at a time), or we can say that we rewrite PE parsing
>> from the ground up. (BTW: I have seriously attempted that in the
>> past, and I gave up, because the PE format is FUBAR.)
>
> [Jiewen] Maybe there is misunderstanding.
> I do not mean to let patch provider to fix all issue in PE parsing.
> Just like we cannot file one Bugzilla to fix all issue in EDKII - it
> is unfair.
>
> What I mean is that the patch provider should guarantee the
> correctness and completeness of the issue in the bug report.
>
> One faked bad example of correctness is:
>     A bug report is file to say: the code has overflow class A.
>     The factor is: the code has overflow class A at line X and line Y.
>     The patch only modified some code at line X, but the overflow
>     class A at line X still exists.
>
> One faked bad example of completeness is:
>     A bug report is file to say: the code has overflow class A.
>     The factor is: the code has overflow class A at line X and line Y.
>     The patch only fixed the overflow class A at line X but not line
>     Y.
>
> The patch provider should take responsibility to do that work
> seriously to find out issue in line X and line Y and fix them.
> He/she may choose to just fix line X and line Y. Rewrite is whole
> module is NOT required.

I agree completely.

My point was that we need the bug report to be precise, in the first
place. If the bug report doesn't clearly identify lines X and Y, we will
likely not get the completeness part right.

"Clearly identify" may mean spelling out lines X and Y specifically. Or
it may mean defining "class A" sufficiently clearly that someone else
reading the affected function can find X and Y themselves.

> If I can give some comment, I would think about the provide the fix in
> BasePeCoffLib.

>From a software design perspective, you are 100% right.

Unfortunately, I can't do it.

That's what I mentioned before -- I had tried rewriting BasePeCoffLib,
because in my opinion, BasePeCoffLib is unsalvageable in its current
form. And I gave up on the rewrite.

Please see the following email. I sent it to some people off-list, on
2020-Feb-14:

> There are currently four (4) TianoCore security BZs (1957, 1990, 1993,
> 2215), embargoed, that describe various ways in which cunningly
> crafted PE images can evade Secure Boot verification.
>
> [...]
>
> Primarily, I just couldn't find my peace with the idea that fixing
> such PE/COFF parsing mistakes (integer overflows, buffer overflows)
> *must* be a one-by-one fixing game. I wanted an approach that would
> fix these *classes* of vulnerabilities, in PE/COFF parsing.
>
> So, beginnning of this February I returned to this topic, and spent
> two days on prototyping / researching a container / interval based
> approach. Here's one of the commit messages, as a way of explaining:
>
>     OvmfPkg/DxePeCoffValidatorLib: introduce CONTAINER type and helper funcs
>
>     For validating the well-formedness of a PE/COFF file, introduce the
>     CONTAINER type, and some workhorse functions. (The functions added in this
>     patch will not be called directly from the code that will process PE/COFF
>     structures.)
>
>     The CONTAINER type describes a contiguous non-empty interval in a PE/COFF
>     file (on-disk representation, or in-memory representation). Containers can
>     be nested. The data from scalar-sized containers can be read out, as part
>     of their creation. For on-disk representations of PE/COFF files, scalar
>     reads are permitted; for in-memory representations, no data access is
>     permitted (only CONTAINER tracking / nesting).
>
>     The goals of CONTAINER are the following:
>
>     - enforce the proper nesting of PE/COFF structures (structure boundaries
>       must not be crossed by runs of data);
>
>     - prevent integer overflows and buffer overflows;
>
>     - prevent zero-size structures;
>
>     - prevent infinite nesting by requiring proper sub-intervals;
>
>     - prevent internal PE/COFF pointers from aliasing each other (unless they
>       point at container and containee structures);
>
>     - terminate nesting at scalar-sized containers;
>
>     - assuming an array of pointers is processed in increasing element order,
>       enforce that the pointed-to objects are located at increasing offsets
>       too;
>
>     - assign human-readable names to PE/COFF structures and fields, for
>       debugging PE/COFF malformations.
>
> Because, several of the vulnerabilities exploited cross-directed and
> aliased internal pointers in PE/COFF files.
>
> Two days of delirious spec reading and coding later, and 2000+ lines
> later, I decided that my idea was unviable. The PE/COFF spec was so
> incredibly mis-designed and crufty that enforcing the *internal*
> consistency of all the size fields and the internal pointers would
> inevitably fall into one of the following categories:
>
> - the checks wouldn't be strict enough, and some nasty images would
>   slip through,
>
> - the checks would be too strict, and some quirky, but valid, images
>   would be unjustifiedly caught.
>
> So I gave up and I've accepted that it remains a whack-a-mole game.
> [...]
>
> (NB: I don't claim that ELF is not similarly brain-damaged.)

So now, I've only considered contributing patches for bug#2215 because
the code in question resides in DxeImageVerificationLib, and *not* in
BasePeCoffLib. I'm not going to touch BasePeCoffLib -- in my opinion,
BasePeCoffLib is unfixable without a complete rewrite.

I would *like* if BasePeCoffLib were fixable incrementally, but I just
don't see how that's possible.

In support of my opinion, please open the following bugzilla ticket:

  https://bugzilla.tianocore.org/show_bug.cgi?id=2643

and search the comments (with the browser's in-page search feature, such
as Ctrl+F) for the following expression:

  new PE loader

I understand exactly what Vitaly and Marvin meant in those comments. :(

Thanks,
Laszlo


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-18 10:17             ` Laszlo Ersek
       [not found]               ` <a7elBrHZ3zD0Stt3MiPOUU_6uOnp-LlR4c9weDhWm4xYH388XWK0M80fLZe_AqbzF68IFK_IdkWQtKN8HKyRnQ==@protonmail.internalid>
@ 2020-08-18 10:24               ` Marvin Häuser
  2020-08-18 11:01                 ` Laszlo Ersek
  2020-08-18 12:21                 ` Vitaly Cheptsov
  2020-08-18 13:12               ` Yao, Jiewen
  2 siblings, 2 replies; 32+ messages in thread
From: Marvin Häuser @ 2020-08-18 10:24 UTC (permalink / raw)
  To: Laszlo Ersek, Yao, Jiewen
  Cc: devel@edk2.groups.io, xiewenyi2@huawei.com, Wang, Jian J,
	huangming23@huawei.com, songdongkuang@huawei.com, Vitaly Cheptsov

Good day everyone,

First off, for your information, I'm sending from my new e-mail address 
from now on.

Please excuse me, I cannot read your entire thread right now, I will 
definitely make sure to catch up as soon as time permits, but I just 
wanted to confirm we are indeed working on a reimplementation of the PE 
loader.
It involves correcting several security issues (which will be detailed 
as the patches are sent as anything else would be too much work for us 
right now), reducing code duplication (how often is the hashing 
algorithm duplicated across edk2? :) ) and a more or less experimental 
approach to formal verification. We plan to submit it this year, however 
please note that this is a low priority project and is not being worked 
on on a full-time basis.

Please let us know about your own plans so we do not end up duplicating 
work.

Best regards,
Marvin

Am 18.08.2020 um 12:17 schrieb Laszlo Ersek:
> Hi Jiewen,
> 
> (+Marvin, +Vitaly)
> 
> On 08/18/20 01:23, Yao, Jiewen wrote:
>>> -----Original Message-----
>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Laszlo
>>> Ersek
>>> Sent: Tuesday, August 18, 2020 12:53 AM
>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
>>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> 
> [...]
> 
>> However, I do think the producer is mandatory for a fix or at least a
>> security fix.
>> The owner to fix the issue should guarantee the patch is good.
>> The owner shall never rely on the code reviewer to figure out if the
>> patch is good and complete.
>>
>> I have some bad experience that bug owner just wrote a patch and tried
>> to fix a problem, without any test.
>> And it happened passed code review from someone who does not well
>> understand the problem, but give rb based upon the time pressure.
>> Later, the fix was approved to be useless.
>>
>> In my memory, at least 3 cases were security fix. They are found, just
>> because they are sensitive, more people took a look later.
>>      It was simple. It was one-line change.
>>     But it has not test, and it was wrong.
>> "It was ridiculous" -- commented by the people who find the so-called
>> security fix does not fix the issue.
> 
> Just because sloppy/rushed reviews exist, and just because reviewers
> operate under time pressure, we should not automatically reject security
> fixes that come without a reproducer.
> 
> Some organizations do develop reproducers, but they never share them
> publicly (for fear of abuse by others).
> 
> But more importantly, in an open development project, a developer could
> have time and expertise to contribute a fix, but not to create a
> reproducer.
> 
> - If we make contributing harder, fewer people will upstream their
>    fixes.
> 
> - If we make contributing harder, then contributions that do make it to
>    the tree will be of higher quality.
> 
> Both statements ring true to me -- so it's a tradeoff.
> 
> (By "we", I mean the edk2 community.)
> 
>>> Additionally, the exact statement that the bug report does make,
>>> namely
>>>
>>>    it's possible to overflow Offset back to 0 causing an endless loop
>>>
>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can
>>> be overflowed to zero, but the *addend* that is added to OffSet can
>>> be overflowed to zero. Therefore the infinite loop will arise because
>>> OffSet remains stuck at its present value, and not because OffSet
>>> will be re-set to zero.
>>>
>>> For the reasons, we can only speculate as to what the actual problem
>>> is, unless Jian decides to join the discussion and clarifies what he
>>> had in mind originally.
>>
>> [Jiewen] Would you please clarify what do you mean "we" here?
>> If "we" means the bug dispatcher, it is totally OK. The dispatcher
>> just assign the bug.
>> If "we" means the developer assigned to fix the bug, it is NOT OK. The
>> developer should take the responsibility to understand the problem.
> 
> By "we", I mean the edk2 community.
> 
>>> We can write a patch based on code analysis. It's possible to
>>> identify integer overflows based on code analysis, and it's possible
>>> to verify the correctness of fixes by code review. Obviously testing
>>> is always good, but many times, constructing reproducers for such
>>> issues that were found by code review, is difficult and time
>>> consuming. We can say that we don't fix vulnerabilities without
>>> reproducers, or we can say that we make an effort to fix them even if
>>> all we have is code analysis (and not a reproducer).
>>
>> [Jiewen] I would say: yes and no.
>> Yes, I agree with you that it might be difficult and time consuming to
>> construct the reproducer.
>> However, "obviously" is a subject term. Someone may think something is
>> obvious, but other people does not.
>> We should be clear the responsibility of the patch provider is to
>> provide high quality patch.
>> Having basic unit test is the best way to prove that the fix is good.
>>
>> I have seen bad cases when I ask for the test for patch, then the
>> answer I got is: "I test the windows boot".
>> But the test - windows boot - has nothing related to the patch. It
>> only proves no regression, but cannot prove the issue described is
>> resolved.
> 
> Right. It would be ideal if every patch came with a unit test. But that
> also means some folks will contribute less.
> 
> Consider normal (not security) patches. We require that all function
> return values be checked (unless it really doesn't matter if a function
> call fails). If a function call fails, we need to roll back the actions
> taken thus far. Release resources and so on. This is why we have the
> "cascade of error handling labels" pattern.
> 
> But, of course, we don't test every possible error path in the code. So
> what's the solution there:
> 
> - reject such patches that carefully construct the error paths, but do
>    not provide unit tests with complete error path coverage?
> 
> - say that we don't care about thorough error paths, so let's just hang,
>    or leak resources, whenever something fails?
> 
> Personally I prefer the detailed error paths. They need to be written
> and reviewed carefully. And they can be accepted even if they are not
> tested with complete coverage.
> 
> Some people think otherwise; they say no untested (untestable) code
> should ever be merged.
> 
> Back to security patches -- creating reproducers usually requires a
> setup (tools, expertise, time allocation etc) that is different from a
> "normal" setup. It may require specialized binary format editors,
> expertise in "penetration testing", and so on.
> 
> I mostly know the C language rules that pertain to integer and buffer
> overflows, so I can usually spot their violations in C code, and propose
> fixes for them too. But I'm not a security researcher, so I don't write
> exploits as a norm -- I don't even investigate, generally speaking, the
> potential practical impact of "undefined behavior". When there's a
> buffer overflow or integer overflow in the code, that's the *end* of the
> story for me, while it's the *start* of the work for a security
> researcher.
> 
> When you require reproducers for all security patches, you restrict the
> potential contributor pool to security researchers.
> 
>> Let's think again in this case, if the patch provider does some basic
>> unit test, he/she may find out the problem by himself/herself.
>> That can save other people's time to review.
>>
>> I don't prefer to move the responsibility from patch provider to the
>> code reviewer to check if the fix is good.
>> Otherwise, the code reviewer may be overwhelmed.
>>
>> We may clarify and document the role and responsibility in EDKII
>> clearly. Once that is ready, we can follow the rule.
>> Before that is ready, in this particular case, I still prefer we have
>> producer to prove the patch is good.
> 
> OK, thanks for explaining.
> 
> Given that I'm unable to create such a PE file (from scratch or by
> modifying another one), I won't post the patches stand-alone.
> 
>>> So the above paragraph concerns "correctness". Regarding
>>> "completeness", I guarantee you that this patch does not fix *all*
>>> problems related to PE parsing. (See the other BZ tickets.) It does
>>> fix *one* issue with PE parsing. We can say that we try to fix such
>>> issues gradually (give different CVE numbers to different issues, and
>>> address them one at a time), or we can say that we rewrite PE parsing
>>> from the ground up. (BTW: I have seriously attempted that in the
>>> past, and I gave up, because the PE format is FUBAR.)
>>
>> [Jiewen] Maybe there is misunderstanding.
>> I do not mean to let patch provider to fix all issue in PE parsing.
>> Just like we cannot file one Bugzilla to fix all issue in EDKII - it
>> is unfair.
>>
>> What I mean is that the patch provider should guarantee the
>> correctness and completeness of the issue in the bug report.
>>
>> One faked bad example of correctness is:
>>      A bug report is file to say: the code has overflow class A.
>>      The factor is: the code has overflow class A at line X and line Y.
>>      The patch only modified some code at line X, but the overflow
>>      class A at line X still exists.
>>
>> One faked bad example of completeness is:
>>      A bug report is file to say: the code has overflow class A.
>>      The factor is: the code has overflow class A at line X and line Y.
>>      The patch only fixed the overflow class A at line X but not line
>>      Y.
>>
>> The patch provider should take responsibility to do that work
>> seriously to find out issue in line X and line Y and fix them.
>> He/she may choose to just fix line X and line Y. Rewrite is whole
>> module is NOT required.
> 
> I agree completely.
> 
> My point was that we need the bug report to be precise, in the first
> place. If the bug report doesn't clearly identify lines X and Y, we will
> likely not get the completeness part right.
> 
> "Clearly identify" may mean spelling out lines X and Y specifically. Or
> it may mean defining "class A" sufficiently clearly that someone else
> reading the affected function can find X and Y themselves.
> 
>> If I can give some comment, I would think about the provide the fix in
>> BasePeCoffLib.
> 
>  From a software design perspective, you are 100% right.
> 
> Unfortunately, I can't do it.
> 
> That's what I mentioned before -- I had tried rewriting BasePeCoffLib,
> because in my opinion, BasePeCoffLib is unsalvageable in its current
> form. And I gave up on the rewrite.
> 
> Please see the following email. I sent it to some people off-list, on
> 2020-Feb-14:
> 
>> There are currently four (4) TianoCore security BZs (1957, 1990, 1993,
>> 2215), embargoed, that describe various ways in which cunningly
>> crafted PE images can evade Secure Boot verification.
>>
>> [...]
>>
>> Primarily, I just couldn't find my peace with the idea that fixing
>> such PE/COFF parsing mistakes (integer overflows, buffer overflows)
>> *must* be a one-by-one fixing game. I wanted an approach that would
>> fix these *classes* of vulnerabilities, in PE/COFF parsing.
>>
>> So, beginnning of this February I returned to this topic, and spent
>> two days on prototyping / researching a container / interval based
>> approach. Here's one of the commit messages, as a way of explaining:
>>
>>      OvmfPkg/DxePeCoffValidatorLib: introduce CONTAINER type and helper funcs
>>
>>      For validating the well-formedness of a PE/COFF file, introduce the
>>      CONTAINER type, and some workhorse functions. (The functions added in this
>>      patch will not be called directly from the code that will process PE/COFF
>>      structures.)
>>
>>      The CONTAINER type describes a contiguous non-empty interval in a PE/COFF
>>      file (on-disk representation, or in-memory representation). Containers can
>>      be nested. The data from scalar-sized containers can be read out, as part
>>      of their creation. For on-disk representations of PE/COFF files, scalar
>>      reads are permitted; for in-memory representations, no data access is
>>      permitted (only CONTAINER tracking / nesting).
>>
>>      The goals of CONTAINER are the following:
>>
>>      - enforce the proper nesting of PE/COFF structures (structure boundaries
>>        must not be crossed by runs of data);
>>
>>      - prevent integer overflows and buffer overflows;
>>
>>      - prevent zero-size structures;
>>
>>      - prevent infinite nesting by requiring proper sub-intervals;
>>
>>      - prevent internal PE/COFF pointers from aliasing each other (unless they
>>        point at container and containee structures);
>>
>>      - terminate nesting at scalar-sized containers;
>>
>>      - assuming an array of pointers is processed in increasing element order,
>>        enforce that the pointed-to objects are located at increasing offsets
>>        too;
>>
>>      - assign human-readable names to PE/COFF structures and fields, for
>>        debugging PE/COFF malformations.
>>
>> Because, several of the vulnerabilities exploited cross-directed and
>> aliased internal pointers in PE/COFF files.
>>
>> Two days of delirious spec reading and coding later, and 2000+ lines
>> later, I decided that my idea was unviable. The PE/COFF spec was so
>> incredibly mis-designed and crufty that enforcing the *internal*
>> consistency of all the size fields and the internal pointers would
>> inevitably fall into one of the following categories:
>>
>> - the checks wouldn't be strict enough, and some nasty images would
>>    slip through,
>>
>> - the checks would be too strict, and some quirky, but valid, images
>>    would be unjustifiedly caught.
>>
>> So I gave up and I've accepted that it remains a whack-a-mole game.
>> [...]
>>
>> (NB: I don't claim that ELF is not similarly brain-damaged.)
> 
> So now, I've only considered contributing patches for bug#2215 because
> the code in question resides in DxeImageVerificationLib, and *not* in
> BasePeCoffLib. I'm not going to touch BasePeCoffLib -- in my opinion,
> BasePeCoffLib is unfixable without a complete rewrite.
> 
> I would *like* if BasePeCoffLib were fixable incrementally, but I just
> don't see how that's possible.
> 
> In support of my opinion, please open the following bugzilla ticket:
> 
>    https://bugzilla.tianocore.org/show_bug.cgi?id=2643
> 
> and search the comments (with the browser's in-page search feature, such
> as Ctrl+F) for the following expression:
> 
>    new PE loader
> 
> I understand exactly what Vitaly and Marvin meant in those comments. :(
> 
> Thanks,
> Laszlo
> 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-18 10:24               ` Marvin Häuser
@ 2020-08-18 11:01                 ` Laszlo Ersek
  2020-08-18 12:21                 ` Vitaly Cheptsov
  1 sibling, 0 replies; 32+ messages in thread
From: Laszlo Ersek @ 2020-08-18 11:01 UTC (permalink / raw)
  To: Marvin Häuser, Yao, Jiewen
  Cc: devel@edk2.groups.io, xiewenyi2@huawei.com, Wang, Jian J,
	huangming23@huawei.com, songdongkuang@huawei.com, Vitaly Cheptsov

On 08/18/20 12:24, Marvin Häuser wrote:
> Good day everyone,
> 
> First off, for your information, I'm sending from my new e-mail address
> from now on.

Thanks -- can you please update your email in bugzilla too? (Before
sending my email, I made sure I'd include your email address as seen in
bugzilla.)

> Please excuse me, I cannot read your entire thread right now, I will
> definitely make sure to catch up as soon as time permits, but I just
> wanted to confirm we are indeed working on a reimplementation of the PE
> loader.

Awesome, thank you.

> It involves correcting several security issues (which will be detailed
> as the patches are sent as anything else would be too much work for us
> right now), reducing code duplication (how often is the hashing
> algorithm duplicated across edk2? :) ) and a more or less experimental
> approach to formal verification. We plan to submit it this year, however
> please note that this is a low priority project and is not being worked
> on on a full-time basis.

OK.

> Please let us know about your own plans so we do not end up duplicating
> work.

I got no plans; I'm just happy that you can work on this (even if with
low priority) where I had to give up.

Thanks!
Laszlo


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-18 10:24               ` Marvin Häuser
  2020-08-18 11:01                 ` Laszlo Ersek
@ 2020-08-18 12:21                 ` Vitaly Cheptsov
  1 sibling, 0 replies; 32+ messages in thread
From: Vitaly Cheptsov @ 2020-08-18 12:21 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: Yao, Jiewen, devel@edk2.groups.io, xiewenyi2@huawei.com,
	Wang, Jian J, huangming23@huawei.com, songdongkuang@huawei.com,
	Marvin Häuser


[-- Attachment #1.1: Type: text/plain, Size: 16279 bytes --]

As a follow-up to Marvin’s e-mail and in the reference of the thread. I should add that to make the result more reliable we not only hope for the reviewer experience and testing, but also write formal proofs for several code properties. This is the primary reason the C code has not yet left the facility, despite being mostly written. If you feel interested, we use an opensource inhouse-written tool, AstraVer[1][2], which is an extension of Frama-C[3].

Best regards,
Vitaly

[1] https://arxiv.org/pdf/1809.00626.pdf <https://arxiv.org/pdf/1809.00626.pdf>
[2] https://www.isprasopen.ru/2018/docs/Volkov.pdf <https://www.isprasopen.ru/2018/docs/Volkov.pdf>
[3] https://frama-c.com <https://frama-c.com/>

> 18 авг. 2020 г., в 13:24, Marvin Häuser <mhaeuser@posteo.de> написал(а):
> 
> 
> Good day everyone,
> 
> First off, for your information, I'm sending from my new e-mail address
> from now on.
> 
> Please excuse me, I cannot read your entire thread right now, I will
> definitely make sure to catch up as soon as time permits, but I just
> wanted to confirm we are indeed working on a reimplementation of the PE
> loader.
> It involves correcting several security issues (which will be detailed
> as the patches are sent as anything else would be too much work for us
> right now), reducing code duplication (how often is the hashing
> algorithm duplicated across edk2? :) ) and a more or less experimental
> approach to formal verification. We plan to submit it this year, however
> please note that this is a low priority project and is not being worked
> on on a full-time basis.
> 
> Please let us know about your own plans so we do not end up duplicating
> work.
> 
> Best regards,
> Marvin
> 
> Am 18.08.2020 um 12:17 schrieb Laszlo Ersek:
>> Hi Jiewen,
>> 
>> (+Marvin, +Vitaly)
>> 
>> On 08/18/20 01:23, Yao, Jiewen wrote:
>>>> -----Original Message-----
>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Laszlo
>>>> Ersek
>>>> Sent: Tuesday, August 18, 2020 12:53 AM
>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
>>>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>> 
>> [...]
>> 
>>> However, I do think the producer is mandatory for a fix or at least a
>>> security fix.
>>> The owner to fix the issue should guarantee the patch is good.
>>> The owner shall never rely on the code reviewer to figure out if the
>>> patch is good and complete.
>>> 
>>> I have some bad experience that bug owner just wrote a patch and tried
>>> to fix a problem, without any test.
>>> And it happened passed code review from someone who does not well
>>> understand the problem, but give rb based upon the time pressure.
>>> Later, the fix was approved to be useless.
>>> 
>>> In my memory, at least 3 cases were security fix. They are found, just
>>> because they are sensitive, more people took a look later.
>>>     It was simple. It was one-line change.
>>>    But it has not test, and it was wrong.
>>> "It was ridiculous" -- commented by the people who find the so-called
>>> security fix does not fix the issue.
>> 
>> Just because sloppy/rushed reviews exist, and just because reviewers
>> operate under time pressure, we should not automatically reject security
>> fixes that come without a reproducer.
>> 
>> Some organizations do develop reproducers, but they never share them
>> publicly (for fear of abuse by others).
>> 
>> But more importantly, in an open development project, a developer could
>> have time and expertise to contribute a fix, but not to create a
>> reproducer.
>> 
>> - If we make contributing harder, fewer people will upstream their
>>   fixes.
>> 
>> - If we make contributing harder, then contributions that do make it to
>>   the tree will be of higher quality.
>> 
>> Both statements ring true to me -- so it's a tradeoff.
>> 
>> (By "we", I mean the edk2 community.)
>> 
>>>> Additionally, the exact statement that the bug report does make,
>>>> namely
>>>> 
>>>>   it's possible to overflow Offset back to 0 causing an endless loop
>>>> 
>>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can
>>>> be overflowed to zero, but the *addend* that is added to OffSet can
>>>> be overflowed to zero. Therefore the infinite loop will arise because
>>>> OffSet remains stuck at its present value, and not because OffSet
>>>> will be re-set to zero.
>>>> 
>>>> For the reasons, we can only speculate as to what the actual problem
>>>> is, unless Jian decides to join the discussion and clarifies what he
>>>> had in mind originally.
>>> 
>>> [Jiewen] Would you please clarify what do you mean "we" here?
>>> If "we" means the bug dispatcher, it is totally OK. The dispatcher
>>> just assign the bug.
>>> If "we" means the developer assigned to fix the bug, it is NOT OK. The
>>> developer should take the responsibility to understand the problem.
>> 
>> By "we", I mean the edk2 community.
>> 
>>>> We can write a patch based on code analysis. It's possible to
>>>> identify integer overflows based on code analysis, and it's possible
>>>> to verify the correctness of fixes by code review. Obviously testing
>>>> is always good, but many times, constructing reproducers for such
>>>> issues that were found by code review, is difficult and time
>>>> consuming. We can say that we don't fix vulnerabilities without
>>>> reproducers, or we can say that we make an effort to fix them even if
>>>> all we have is code analysis (and not a reproducer).
>>> 
>>> [Jiewen] I would say: yes and no.
>>> Yes, I agree with you that it might be difficult and time consuming to
>>> construct the reproducer.
>>> However, "obviously" is a subject term. Someone may think something is
>>> obvious, but other people does not.
>>> We should be clear the responsibility of the patch provider is to
>>> provide high quality patch.
>>> Having basic unit test is the best way to prove that the fix is good.
>>> 
>>> I have seen bad cases when I ask for the test for patch, then the
>>> answer I got is: "I test the windows boot".
>>> But the test - windows boot - has nothing related to the patch. It
>>> only proves no regression, but cannot prove the issue described is
>>> resolved.
>> 
>> Right. It would be ideal if every patch came with a unit test. But that
>> also means some folks will contribute less.
>> 
>> Consider normal (not security) patches. We require that all function
>> return values be checked (unless it really doesn't matter if a function
>> call fails). If a function call fails, we need to roll back the actions
>> taken thus far. Release resources and so on. This is why we have the
>> "cascade of error handling labels" pattern.
>> 
>> But, of course, we don't test every possible error path in the code. So
>> what's the solution there:
>> 
>> - reject such patches that carefully construct the error paths, but do
>>   not provide unit tests with complete error path coverage?
>> 
>> - say that we don't care about thorough error paths, so let's just hang,
>>   or leak resources, whenever something fails?
>> 
>> Personally I prefer the detailed error paths. They need to be written
>> and reviewed carefully. And they can be accepted even if they are not
>> tested with complete coverage.
>> 
>> Some people think otherwise; they say no untested (untestable) code
>> should ever be merged.
>> 
>> Back to security patches -- creating reproducers usually requires a
>> setup (tools, expertise, time allocation etc) that is different from a
>> "normal" setup. It may require specialized binary format editors,
>> expertise in "penetration testing", and so on.
>> 
>> I mostly know the C language rules that pertain to integer and buffer
>> overflows, so I can usually spot their violations in C code, and propose
>> fixes for them too. But I'm not a security researcher, so I don't write
>> exploits as a norm -- I don't even investigate, generally speaking, the
>> potential practical impact of "undefined behavior". When there's a
>> buffer overflow or integer overflow in the code, that's the *end* of the
>> story for me, while it's the *start* of the work for a security
>> researcher.
>> 
>> When you require reproducers for all security patches, you restrict the
>> potential contributor pool to security researchers.
>> 
>>> Let's think again in this case, if the patch provider does some basic
>>> unit test, he/she may find out the problem by himself/herself.
>>> That can save other people's time to review.
>>> 
>>> I don't prefer to move the responsibility from patch provider to the
>>> code reviewer to check if the fix is good.
>>> Otherwise, the code reviewer may be overwhelmed.
>>> 
>>> We may clarify and document the role and responsibility in EDKII
>>> clearly. Once that is ready, we can follow the rule.
>>> Before that is ready, in this particular case, I still prefer we have
>>> producer to prove the patch is good.
>> 
>> OK, thanks for explaining.
>> 
>> Given that I'm unable to create such a PE file (from scratch or by
>> modifying another one), I won't post the patches stand-alone.
>> 
>>>> So the above paragraph concerns "correctness". Regarding
>>>> "completeness", I guarantee you that this patch does not fix *all*
>>>> problems related to PE parsing. (See the other BZ tickets.) It does
>>>> fix *one* issue with PE parsing. We can say that we try to fix such
>>>> issues gradually (give different CVE numbers to different issues, and
>>>> address them one at a time), or we can say that we rewrite PE parsing
>>>> from the ground up. (BTW: I have seriously attempted that in the
>>>> past, and I gave up, because the PE format is FUBAR.)
>>> 
>>> [Jiewen] Maybe there is misunderstanding.
>>> I do not mean to let patch provider to fix all issue in PE parsing.
>>> Just like we cannot file one Bugzilla to fix all issue in EDKII - it
>>> is unfair.
>>> 
>>> What I mean is that the patch provider should guarantee the
>>> correctness and completeness of the issue in the bug report.
>>> 
>>> One faked bad example of correctness is:
>>>     A bug report is file to say: the code has overflow class A.
>>>     The factor is: the code has overflow class A at line X and line Y.
>>>     The patch only modified some code at line X, but the overflow
>>>     class A at line X still exists.
>>> 
>>> One faked bad example of completeness is:
>>>     A bug report is file to say: the code has overflow class A.
>>>     The factor is: the code has overflow class A at line X and line Y.
>>>     The patch only fixed the overflow class A at line X but not line
>>>     Y.
>>> 
>>> The patch provider should take responsibility to do that work
>>> seriously to find out issue in line X and line Y and fix them.
>>> He/she may choose to just fix line X and line Y. Rewrite is whole
>>> module is NOT required.
>> 
>> I agree completely.
>> 
>> My point was that we need the bug report to be precise, in the first
>> place. If the bug report doesn't clearly identify lines X and Y, we will
>> likely not get the completeness part right.
>> 
>> "Clearly identify" may mean spelling out lines X and Y specifically. Or
>> it may mean defining "class A" sufficiently clearly that someone else
>> reading the affected function can find X and Y themselves.
>> 
>>> If I can give some comment, I would think about the provide the fix in
>>> BasePeCoffLib.
>> 
>> From a software design perspective, you are 100% right.
>> 
>> Unfortunately, I can't do it.
>> 
>> That's what I mentioned before -- I had tried rewriting BasePeCoffLib,
>> because in my opinion, BasePeCoffLib is unsalvageable in its current
>> form. And I gave up on the rewrite.
>> 
>> Please see the following email. I sent it to some people off-list, on
>> 2020-Feb-14:
>> 
>>> There are currently four (4) TianoCore security BZs (1957, 1990, 1993,
>>> 2215), embargoed, that describe various ways in which cunningly
>>> crafted PE images can evade Secure Boot verification.
>>> 
>>> [...]
>>> 
>>> Primarily, I just couldn't find my peace with the idea that fixing
>>> such PE/COFF parsing mistakes (integer overflows, buffer overflows)
>>> *must* be a one-by-one fixing game. I wanted an approach that would
>>> fix these *classes* of vulnerabilities, in PE/COFF parsing.
>>> 
>>> So, beginnning of this February I returned to this topic, and spent
>>> two days on prototyping / researching a container / interval based
>>> approach. Here's one of the commit messages, as a way of explaining:
>>> 
>>>     OvmfPkg/DxePeCoffValidatorLib: introduce CONTAINER type and helper funcs
>>> 
>>>     For validating the well-formedness of a PE/COFF file, introduce the
>>>     CONTAINER type, and some workhorse functions. (The functions added in this
>>>     patch will not be called directly from the code that will process PE/COFF
>>>     structures.)
>>> 
>>>     The CONTAINER type describes a contiguous non-empty interval in a PE/COFF
>>>     file (on-disk representation, or in-memory representation). Containers can
>>>     be nested. The data from scalar-sized containers can be read out, as part
>>>     of their creation. For on-disk representations of PE/COFF files, scalar
>>>     reads are permitted; for in-memory representations, no data access is
>>>     permitted (only CONTAINER tracking / nesting).
>>> 
>>>     The goals of CONTAINER are the following:
>>> 
>>>     - enforce the proper nesting of PE/COFF structures (structure boundaries
>>>       must not be crossed by runs of data);
>>> 
>>>     - prevent integer overflows and buffer overflows;
>>> 
>>>     - prevent zero-size structures;
>>> 
>>>     - prevent infinite nesting by requiring proper sub-intervals;
>>> 
>>>     - prevent internal PE/COFF pointers from aliasing each other (unless they
>>>       point at container and containee structures);
>>> 
>>>     - terminate nesting at scalar-sized containers;
>>> 
>>>     - assuming an array of pointers is processed in increasing element order,
>>>       enforce that the pointed-to objects are located at increasing offsets
>>>       too;
>>> 
>>>     - assign human-readable names to PE/COFF structures and fields, for
>>>       debugging PE/COFF malformations.
>>> 
>>> Because, several of the vulnerabilities exploited cross-directed and
>>> aliased internal pointers in PE/COFF files.
>>> 
>>> Two days of delirious spec reading and coding later, and 2000+ lines
>>> later, I decided that my idea was unviable. The PE/COFF spec was so
>>> incredibly mis-designed and crufty that enforcing the *internal*
>>> consistency of all the size fields and the internal pointers would
>>> inevitably fall into one of the following categories:
>>> 
>>> - the checks wouldn't be strict enough, and some nasty images would
>>>   slip through,
>>> 
>>> - the checks would be too strict, and some quirky, but valid, images
>>>   would be unjustifiedly caught.
>>> 
>>> So I gave up and I've accepted that it remains a whack-a-mole game.
>>> [...]
>>> 
>>> (NB: I don't claim that ELF is not similarly brain-damaged.)
>> 
>> So now, I've only considered contributing patches for bug#2215 because
>> the code in question resides in DxeImageVerificationLib, and *not* in
>> BasePeCoffLib. I'm not going to touch BasePeCoffLib -- in my opinion,
>> BasePeCoffLib is unfixable without a complete rewrite.
>> 
>> I would *like* if BasePeCoffLib were fixable incrementally, but I just
>> don't see how that's possible.
>> 
>> In support of my opinion, please open the following bugzilla ticket:
>> 
>>   https://bugzilla.tianocore.org/show_bug.cgi?id=2643
>> 
>> and search the comments (with the browser's in-page search feature, such
>> as Ctrl+F) for the following expression:
>> 
>>   new PE loader
>> 
>> I understand exactly what Vitaly and Marvin meant in those comments. :(
>> 
>> Thanks,
>> Laszlo
>> 


[-- Attachment #1.2: Type: text/html, Size: 21603 bytes --]

[-- Attachment #2: Message signed with OpenPGP --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-18 10:17             ` Laszlo Ersek
       [not found]               ` <a7elBrHZ3zD0Stt3MiPOUU_6uOnp-LlR4c9weDhWm4xYH388XWK0M80fLZe_AqbzF68IFK_IdkWQtKN8HKyRnQ==@protonmail.internalid>
  2020-08-18 10:24               ` Marvin Häuser
@ 2020-08-18 13:12               ` Yao, Jiewen
  2020-08-18 17:29                 ` Bret Barkelew
  2 siblings, 1 reply; 32+ messages in thread
From: Yao, Jiewen @ 2020-08-18 13:12 UTC (permalink / raw)
  To: devel@edk2.groups.io, lersek@redhat.com
  Cc: xiewenyi2@huawei.com, Wang, Jian J, huangming23@huawei.com,
	songdongkuang@huawei.com, Marvin Häuser, Vitaly Cheptsov

Hi Laszlo
I agree with on your most points.
It is all about *return of investment* or *risk control*. Like we cannot pursue 100% security because we will bankrupt ourselves if so.

Here I just raise my concern.
1) If we alway allow developers’ low quality patch without test, the overall quality will become lower and lower. Personally I have no confidence to help catch all those issues. You are the role model on code review. But not all people review the code like you. We need both expertise and time for code review. 

2) I purposely separate security fix from non security one, because the process is different. The embargo might be 6 months. What if we found the security patch does not fix the problem after embargo? Unlike we send one more patch tomorrow, we need wait for another 6 months...

thank you!
Yao, Jiewen


> 在 2020年8月18日,下午6:17,Laszlo Ersek <lersek@redhat.com> 写道:
> 
> Hi Jiewen,
> 
> (+Marvin, +Vitaly)
> 
> On 08/18/20 01:23, Yao, Jiewen wrote:
>>> -----Original Message-----
>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Laszlo
>>> Ersek
>>> Sent: Tuesday, August 18, 2020 12:53 AM
>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
>>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> 
> [...]
> 
>> However, I do think the producer is mandatory for a fix or at least a
>> security fix.
>> The owner to fix the issue should guarantee the patch is good.
>> The owner shall never rely on the code reviewer to figure out if the
>> patch is good and complete.
>> 
>> I have some bad experience that bug owner just wrote a patch and tried
>> to fix a problem, without any test.
>> And it happened passed code review from someone who does not well
>> understand the problem, but give rb based upon the time pressure.
>> Later, the fix was approved to be useless.
>> 
>> In my memory, at least 3 cases were security fix. They are found, just
>> because they are sensitive, more people took a look later.
>>    It was simple. It was one-line change.
>>   But it has not test, and it was wrong.
>> "It was ridiculous" -- commented by the people who find the so-called
>> security fix does not fix the issue.
> 
> Just because sloppy/rushed reviews exist, and just because reviewers
> operate under time pressure, we should not automatically reject security
> fixes that come without a reproducer.
> 
> Some organizations do develop reproducers, but they never share them
> publicly (for fear of abuse by others).
> 
> But more importantly, in an open development project, a developer could
> have time and expertise to contribute a fix, but not to create a
> reproducer.
> 
> - If we make contributing harder, fewer people will upstream their
>  fixes.
> 
> - If we make contributing harder, then contributions that do make it to
>  the tree will be of higher quality.
> 
> Both statements ring true to me -- so it's a tradeoff.
> 
> (By "we", I mean the edk2 community.)
> 
>>> Additionally, the exact statement that the bug report does make,
>>> namely
>>> 
>>>  it's possible to overflow Offset back to 0 causing an endless loop
>>> 
>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can
>>> be overflowed to zero, but the *addend* that is added to OffSet can
>>> be overflowed to zero. Therefore the infinite loop will arise because
>>> OffSet remains stuck at its present value, and not because OffSet
>>> will be re-set to zero.
>>> 
>>> For the reasons, we can only speculate as to what the actual problem
>>> is, unless Jian decides to join the discussion and clarifies what he
>>> had in mind originally.
>> 
>> [Jiewen] Would you please clarify what do you mean "we" here?
>> If "we" means the bug dispatcher, it is totally OK. The dispatcher
>> just assign the bug.
>> If "we" means the developer assigned to fix the bug, it is NOT OK. The
>> developer should take the responsibility to understand the problem.
> 
> By "we", I mean the edk2 community.
> 
>>> We can write a patch based on code analysis. It's possible to
>>> identify integer overflows based on code analysis, and it's possible
>>> to verify the correctness of fixes by code review. Obviously testing
>>> is always good, but many times, constructing reproducers for such
>>> issues that were found by code review, is difficult and time
>>> consuming. We can say that we don't fix vulnerabilities without
>>> reproducers, or we can say that we make an effort to fix them even if
>>> all we have is code analysis (and not a reproducer).
>> 
>> [Jiewen] I would say: yes and no.
>> Yes, I agree with you that it might be difficult and time consuming to
>> construct the reproducer.
>> However, "obviously" is a subject term. Someone may think something is
>> obvious, but other people does not.
>> We should be clear the responsibility of the patch provider is to
>> provide high quality patch.
>> Having basic unit test is the best way to prove that the fix is good.
>> 
>> I have seen bad cases when I ask for the test for patch, then the
>> answer I got is: "I test the windows boot".
>> But the test - windows boot - has nothing related to the patch. It
>> only proves no regression, but cannot prove the issue described is
>> resolved.
> 
> Right. It would be ideal if every patch came with a unit test. But that
> also means some folks will contribute less.
> 
> Consider normal (not security) patches. We require that all function
> return values be checked (unless it really doesn't matter if a function
> call fails). If a function call fails, we need to roll back the actions
> taken thus far. Release resources and so on. This is why we have the
> "cascade of error handling labels" pattern.
> 
> But, of course, we don't test every possible error path in the code. So
> what's the solution there:
> 
> - reject such patches that carefully construct the error paths, but do
>  not provide unit tests with complete error path coverage?
> 
> - say that we don't care about thorough error paths, so let's just hang,
>  or leak resources, whenever something fails?
> 
> Personally I prefer the detailed error paths. They need to be written
> and reviewed carefully. And they can be accepted even if they are not
> tested with complete coverage.
> 
> Some people think otherwise; they say no untested (untestable) code
> should ever be merged.
> 
> Back to security patches -- creating reproducers usually requires a
> setup (tools, expertise, time allocation etc) that is different from a
> "normal" setup. It may require specialized binary format editors,
> expertise in "penetration testing", and so on.
> 
> I mostly know the C language rules that pertain to integer and buffer
> overflows, so I can usually spot their violations in C code, and propose
> fixes for them too. But I'm not a security researcher, so I don't write
> exploits as a norm -- I don't even investigate, generally speaking, the
> potential practical impact of "undefined behavior". When there's a
> buffer overflow or integer overflow in the code, that's the *end* of the
> story for me, while it's the *start* of the work for a security
> researcher.
> 
> When you require reproducers for all security patches, you restrict the
> potential contributor pool to security researchers.
> 
>> Let's think again in this case, if the patch provider does some basic
>> unit test, he/she may find out the problem by himself/herself.
>> That can save other people's time to review.
>> 
>> I don't prefer to move the responsibility from patch provider to the
>> code reviewer to check if the fix is good.
>> Otherwise, the code reviewer may be overwhelmed.
>> 
>> We may clarify and document the role and responsibility in EDKII
>> clearly. Once that is ready, we can follow the rule.
>> Before that is ready, in this particular case, I still prefer we have
>> producer to prove the patch is good.
> 
> OK, thanks for explaining.
> 
> Given that I'm unable to create such a PE file (from scratch or by
> modifying another one), I won't post the patches stand-alone.
> 
>>> So the above paragraph concerns "correctness". Regarding
>>> "completeness", I guarantee you that this patch does not fix *all*
>>> problems related to PE parsing. (See the other BZ tickets.) It does
>>> fix *one* issue with PE parsing. We can say that we try to fix such
>>> issues gradually (give different CVE numbers to different issues, and
>>> address them one at a time), or we can say that we rewrite PE parsing
>>> from the ground up. (BTW: I have seriously attempted that in the
>>> past, and I gave up, because the PE format is FUBAR.)
>> 
>> [Jiewen] Maybe there is misunderstanding.
>> I do not mean to let patch provider to fix all issue in PE parsing.
>> Just like we cannot file one Bugzilla to fix all issue in EDKII - it
>> is unfair.
>> 
>> What I mean is that the patch provider should guarantee the
>> correctness and completeness of the issue in the bug report.
>> 
>> One faked bad example of correctness is:
>>    A bug report is file to say: the code has overflow class A.
>>    The factor is: the code has overflow class A at line X and line Y.
>>    The patch only modified some code at line X, but the overflow
>>    class A at line X still exists.
>> 
>> One faked bad example of completeness is:
>>    A bug report is file to say: the code has overflow class A.
>>    The factor is: the code has overflow class A at line X and line Y.
>>    The patch only fixed the overflow class A at line X but not line
>>    Y.
>> 
>> The patch provider should take responsibility to do that work
>> seriously to find out issue in line X and line Y and fix them.
>> He/she may choose to just fix line X and line Y. Rewrite is whole
>> module is NOT required.
> 
> I agree completely.
> 
> My point was that we need the bug report to be precise, in the first
> place. If the bug report doesn't clearly identify lines X and Y, we will
> likely not get the completeness part right.
> 
> "Clearly identify" may mean spelling out lines X and Y specifically. Or
> it may mean defining "class A" sufficiently clearly that someone else
> reading the affected function can find X and Y themselves.
> 
>> If I can give some comment, I would think about the provide the fix in
>> BasePeCoffLib.
> 
> From a software design perspective, you are 100% right.
> 
> Unfortunately, I can't do it.
> 
> That's what I mentioned before -- I had tried rewriting BasePeCoffLib,
> because in my opinion, BasePeCoffLib is unsalvageable in its current
> form. And I gave up on the rewrite.
> 
> Please see the following email. I sent it to some people off-list, on
> 2020-Feb-14:
> 
>> There are currently four (4) TianoCore security BZs (1957, 1990, 1993,
>> 2215), embargoed, that describe various ways in which cunningly
>> crafted PE images can evade Secure Boot verification.
>> 
>> [...]
>> 
>> Primarily, I just couldn't find my peace with the idea that fixing
>> such PE/COFF parsing mistakes (integer overflows, buffer overflows)
>> *must* be a one-by-one fixing game. I wanted an approach that would
>> fix these *classes* of vulnerabilities, in PE/COFF parsing.
>> 
>> So, beginnning of this February I returned to this topic, and spent
>> two days on prototyping / researching a container / interval based
>> approach. Here's one of the commit messages, as a way of explaining:
>> 
>>    OvmfPkg/DxePeCoffValidatorLib: introduce CONTAINER type and helper funcs
>> 
>>    For validating the well-formedness of a PE/COFF file, introduce the
>>    CONTAINER type, and some workhorse functions. (The functions added in this
>>    patch will not be called directly from the code that will process PE/COFF
>>    structures.)
>> 
>>    The CONTAINER type describes a contiguous non-empty interval in a PE/COFF
>>    file (on-disk representation, or in-memory representation). Containers can
>>    be nested. The data from scalar-sized containers can be read out, as part
>>    of their creation. For on-disk representations of PE/COFF files, scalar
>>    reads are permitted; for in-memory representations, no data access is
>>    permitted (only CONTAINER tracking / nesting).
>> 
>>    The goals of CONTAINER are the following:
>> 
>>    - enforce the proper nesting of PE/COFF structures (structure boundaries
>>      must not be crossed by runs of data);
>> 
>>    - prevent integer overflows and buffer overflows;
>> 
>>    - prevent zero-size structures;
>> 
>>    - prevent infinite nesting by requiring proper sub-intervals;
>> 
>>    - prevent internal PE/COFF pointers from aliasing each other (unless they
>>      point at container and containee structures);
>> 
>>    - terminate nesting at scalar-sized containers;
>> 
>>    - assuming an array of pointers is processed in increasing element order,
>>      enforce that the pointed-to objects are located at increasing offsets
>>      too;
>> 
>>    - assign human-readable names to PE/COFF structures and fields, for
>>      debugging PE/COFF malformations.
>> 
>> Because, several of the vulnerabilities exploited cross-directed and
>> aliased internal pointers in PE/COFF files.
>> 
>> Two days of delirious spec reading and coding later, and 2000+ lines
>> later, I decided that my idea was unviable. The PE/COFF spec was so
>> incredibly mis-designed and crufty that enforcing the *internal*
>> consistency of all the size fields and the internal pointers would
>> inevitably fall into one of the following categories:
>> 
>> - the checks wouldn't be strict enough, and some nasty images would
>>  slip through,
>> 
>> - the checks would be too strict, and some quirky, but valid, images
>>  would be unjustifiedly caught.
>> 
>> So I gave up and I've accepted that it remains a whack-a-mole game.
>> [...]
>> 
>> (NB: I don't claim that ELF is not similarly brain-damaged.)
> 
> So now, I've only considered contributing patches for bug#2215 because
> the code in question resides in DxeImageVerificationLib, and *not* in
> BasePeCoffLib. I'm not going to touch BasePeCoffLib -- in my opinion,
> BasePeCoffLib is unfixable without a complete rewrite.
> 
> I would *like* if BasePeCoffLib were fixable incrementally, but I just
> don't see how that's possible.
> 
> In support of my opinion, please open the following bugzilla ticket:
> 
>  https://bugzilla.tianocore.org/show_bug.cgi?id=2643
> 
> and search the comments (with the browser's in-page search feature, such
> as Ctrl+F) for the following expression:
> 
>  new PE loader
> 
> I understand exactly what Vitaly and Marvin meant in those comments. :(
> 
> Thanks,
> Laszlo
> 
> 
> 
> 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-18  8:58             ` Laszlo Ersek
@ 2020-08-18 15:18               ` john.mathews
  2020-08-19  9:26                 ` Laszlo Ersek
  0 siblings, 1 reply; 32+ messages in thread
From: john.mathews @ 2020-08-18 15:18 UTC (permalink / raw)
  To: Laszlo Ersek, Wang, Jian J, devel@edk2.groups.io, Yao, Jiewen,
	xiewenyi2@huawei.com
  Cc: huangming23@huawei.com, songdongkuang@huawei.com

I dug up the original report details.  This was noted as a concern during a source code inspection.  There was no demonstration of how it might be triggered.

" There is an integer overflow vulnerability in the DxeImageVerificationHandler function when
parsing the PE files attribute certificate table. In cases where WinCertificate->dwLength is
sufficiently large, it's possible to overflow Offset back to 0 causing an endless loop."

The recommendation was to add stricter checking of "Offset" and the embedded length fields of certificate data
before using them.



-----Original Message-----
From: Laszlo Ersek <lersek@redhat.com> 
Sent: Tuesday, August 18, 2020 1:59 AM
To: Wang, Jian J <jian.j.wang@intel.com>; devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>; xiewenyi2@huawei.com
Cc: huangming23@huawei.com; songdongkuang@huawei.com; Mathews, John <john.mathews@intel.com>
Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset

On 08/18/20 04:10, Wang, Jian J wrote:
> Laszlo,
> 
> My apologies for the slow response. I'm not the original reporter but 
> just the BZ submitter. And I didn't do deep analysis to this issue. 
> The issues was reported from one internal team. Add John in loop to see if he knows more about it or not.
> 
> My superficial understanding on such issue is that, if there's 
> "potential" issue in theory and hard to reproduce, it's still worthy 
> of using an alternative way to replace the original implementation 
> with no "potential" issue at all. Maybe we don't have to prove old way is something wrong but must prove that the new way is really safe.

I agree, thanks.

It would be nice to hear more from the internal team about the originally reported (even if hard-to-trigger) issue.

Thanks!
Laszlo

> 
> Regards,
> Jian
> 
>> -----Original Message-----
>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Laszlo 
>> Ersek
>> Sent: Tuesday, August 18, 2020 12:53 AM
>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; 
>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1] 
>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>
>> Hi Jiewen,
>>
>> On 08/14/20 10:53, Yao, Jiewen wrote:
>>>> To Jiewen,
>>>> Sorry, I don't have environment to reproduce the issue.
>>>
>>> Please help me understand, if you don’t have environment to 
>>> reproduce the
>> issue, how do you guarantee that your patch does fix the problem and 
>> we don’t have any other vulnerabilities?
>>
>> The original bug report in
>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is seriously 
>> lacking. It does not go into detail about the alleged integer overflow.
>> It does not quote the code, does not explain the control flow, does 
>> not identify the exact edk2 commit at which the vulnerability exists.
>>
>> The bug report also does not offer a reproducer.
>>
>> Additionally, the exact statement that the bug report does make, 
>> namely
>>
>>   it's possible to overflow Offset back to 0 causing an endless loop
>>
>> is wrong (as far as I can tell anyway). It is not "OffSet" that can 
>> be overflowed to zero, but the *addend* that is added to OffSet can 
>> be overflowed to zero. Therefore the infinite loop will arise because 
>> OffSet remains stuck at its present value, and not because OffSet 
>> will be re-set to zero.
>>
>> For the reasons, we can only speculate as to what the actual problem 
>> is, unless Jian decides to join the discussion and clarifies what he 
>> had in mind originally.
>>
>> My understanding (or even "reconstruction") of the vulnerability is 
>> described above, and in the patches that I proposed.
>>
>> We can write a patch based on code analysis. It's possible to 
>> identify integer overflows based on code analysis, and it's possible 
>> to verify the correctness of fixes by code review. Obviously testing 
>> is always good, but many times, constructing reproducers for such 
>> issues that were found by code review, is difficult and time 
>> consuming. We can say that we don't fix vulnerabilities without 
>> reproducers, or we can say that we make an effort to fix them even if 
>> all we have is code analysis (and not a reproducer).
>>
>> So the above paragraph concerns "correctness". Regarding 
>> "completeness", I guarantee you that this patch does not fix *all* 
>> problems related to PE parsing. (See the other BZ tickets.) It does 
>> fix *one* issue with PE parsing. We can say that we try to fix such 
>> issues gradually (give different CVE numbers to different issues, and 
>> address them one at a time), or we can say that we rewrite PE parsing from the ground up.
>> (BTW: I have seriously attempted that in the past, and I gave up, 
>> because the PE format is FUBAR.)
>>
>> In summary:
>>
>> - the problem statement is unclear,
>>
>> - it seems like there is indeed an integer overflow problem in the 
>> SecDataDir parsing loop, but it's uncertain whether the bug reporter 
>> had exactly that in mind
>>
>> - PE parsing is guaranteed to have other vulnerabilities elsewhere in 
>> edk2, but I'm currently unaware of other such issues in 
>> DxeImageVerificationLib specifically
>>
>> - even if there are other such problems (in DxeImageVerificationLib 
>> or elswehere), fixing this bug that we know about is likely 
>> worthwhile
>>
>> - for many such bugs, constructing a reproducer is difficult and time 
>> consuming; code analysis, and *regression-testing* are frequently the 
>> only tools we have. That doesn't mean we should ignore this class of bugs.
>>
>> (Fixing integer overflows retro-actively is more difficult than 
>> writing overflow-free code in the first place, but that ship has 
>> sailed; so we can only fight these bugs incrementally now, unless we 
>> can rewrite PE parsing with a new data structure from the ground up. 
>> Again I tried that and gave up, because the spec is not public, and 
>> what I did manage to learn about PE, showed that it was insanely 
>> over-engineered. I'm not saying that other binary / executable 
>> formats are better, of course.)
>>
>> Please check out my patches (inlined elsewhere in this thread), and 
>> comment whether you'd like me to post them to the list as a 
>> standalone series.
>>
>> Jian: it wouldn't hurt if you commented as well.
>>
>> Thanks
>> Laszlo
>>
>>>> -----Original Message-----
>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>> wenyi,xie
>>>> via groups.io
>>>> Sent: Friday, August 14, 2020 3:54 PM
>>>> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io; Yao, 
>>>> Jiewen <jiewen.yao@intel.com>; Wang, Jian J <jian.j.wang@intel.com>
>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1] 
>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>
>>>> To Laszlo,
>>>> Thank you for your detailed description, I agree with what you 
>>>> analyzed and
>> I'm
>>>> OK with your patches, it's
>>>> correct and much simpler.
>>>>
>>>> To Jiewen,
>>>> Sorry, I don't have environment to reproduce the issue.
>>>>
>>>> Thanks
>>>> Wenyi
>>>>
>>>> On 2020/8/14 2:50, Laszlo Ersek wrote:
>>>>> On 08/13/20 13:55, Wenyi Xie wrote:
>>>>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
>>>>>>
>>>>>> There is an integer overflow vulnerability in 
>>>>>> DxeImageVerificationHandler function when parsing the PE files 
>>>>>> attribute certificate table. In cases where 
>>>>>> WinCertificate->dwLength is sufficiently large, it's possible to overflow Offset back to 0 causing an endless loop.
>>>>>>
>>>>>> Check offset inbetween VirtualAddress and VirtualAddress + Size.
>>>>>> Using SafeintLib to do offset addition with result check.
>>>>>>
>>>>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>>>>>> Cc: Jian J Wang <jian.j.wang@intel.com>
>>>>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>>>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
>>>>>> ---
>>>>>>  
>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.inf
>> |
>>>> 1 +
>>>>>>  
>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.h
>> |
>>>> 1 +
>>>>>>  
>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.c
>> |
>>>> 111 +++++++++++---------
>>>>>>  3 files changed, 63 insertions(+), 50 deletions(-)
>>>>>>
>>>>>> diff --git
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.inf 
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.inf
>>>>>> index 1e1a639857e0..a7ac4830b3d4 100644
>>>>>> ---
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.inf
>>>>>> +++
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.inf
>>>>>> @@ -53,6 +53,7 @@ [LibraryClasses]
>>>>>>    SecurityManagementLib
>>>>>>    PeCoffLib
>>>>>>    TpmMeasurementLib
>>>>>> +  SafeIntLib
>>>>>>
>>>>>>  [Protocols]
>>>>>>    gEfiFirmwareVolume2ProtocolGuid       ## SOMETIMES_CONSUMES
>>>>>> diff --git
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.h 
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.h
>>>>>> index 17955ff9774c..060273917d5d 100644
>>>>>> ---
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.h
>>>>>> +++
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.h
>>>>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent  
>>>>>> #include <Library/DevicePathLib.h>  #include 
>>>>>> <Library/SecurityManagementLib.h>  #include <Library/PeCoffLib.h>
>>>>>> +#include <Library/SafeIntLib.h>
>>>>>>  #include <Protocol/FirmwareVolume2.h>  #include 
>>>>>> <Protocol/DevicePath.h>  #include <Protocol/BlockIo.h> diff --git
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.c 
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.c
>>>>>> index 36b87e16d53d..dbc03e28c05b 100644
>>>>>> ---
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>> .c
>>>>>> +++
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.c
>>>>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
>>>>>>    EFI_STATUS                           HashStatus;
>>>>>>    EFI_STATUS                           DbStatus;
>>>>>>    BOOLEAN                              IsFound;
>>>>>> +  UINT32                               AlignedLength;
>>>>>> +  UINT32                               Result;
>>>>>> +  EFI_STATUS                           AddStatus;
>>>>>> +  BOOLEAN                              IsAuthDataAssigned;
>>>>>>
>>>>>>    SignatureList     = NULL;
>>>>>>    SignatureListSize = 0;
>>>>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
>>>>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
>>>>>>    IsVerified        = FALSE;
>>>>>>    IsFound           = FALSE;
>>>>>> +  Result            = 0;
>>>>>>
>>>>>>    //
>>>>>>    // Check the image type and get policy setting.
>>>>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
>>>>>>    // The first certificate starts at offset 
>>>>>> (SecDataDir->VirtualAddress) from
>> the
>>>> start of the file.
>>>>>>    //
>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>>>> dwLength))) {
>>>>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet < 
>>>>>> + (SecDataDir-
>>>>> VirtualAddress + SecDataDir->Size));) {
>>>>>> +    IsAuthDataAssigned = FALSE;
>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE
>> (WinCertificate-
>>>>> dwLength);
>>>>>
>>>>> I disagree with this patch.
>>>>>
>>>>> The primary reason for my disagreement is that the bug report 
>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is 
>>>>> inexact, and so this patch tries to fix the wrong thing.
>>>>>
>>>>> With edk2 master at commit 65904cdbb33c, it is *not* possible to 
>>>>> overflow the OffSet variable to zero with "WinCertificate->dwLength"
>>>>> *purely*, and cause an endless loop. Note that we have (at commit
>>>>> 65904cdbb33c):
>>>>>
>>>>>   for (OffSet = SecDataDir->VirtualAddress;
>>>>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE 
>>>>> (WinCertificate-
>>>>> dwLength))) {
>>>>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) 
>>>>> <= sizeof
>>>> (WIN_CERTIFICATE) ||
>>>>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>> WinCertificate-
>>>>> dwLength) {
>>>>>       break;
>>>>>     }
>>>>>
>>>>> The last sub-condition checks whether the Security Data Directory 
>>>>> has enough room left for "WinCertificate->dwLength". If not, then 
>>>>> we break out of the loop.
>>>>>
>>>>> If we *do* have enough room, that is:
>>>>>
>>>>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >=
>> WinCertificate-
>>>>> dwLength
>>>>>
>>>>> then we have (by adding OffSet to both sides):
>>>>>
>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet + 
>>>>> WinCertificate- dwLength
>>>>>
>>>>> The left hand side is a known-good UINT32, and so incrementing 
>>>>> OffSet (a
>>>>> UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32) 
>>>>> does not cause an overflow.
>>>>>
>>>>> Instead, the problem is with the alignment. The "if" statement 
>>>>> checks whether we have enough room for "dwLength", but then 
>>>>> "OffSet" is advanced by "dwLength" *aligned up* to the next 
>>>>> multiple of 8. And that may indeed cause various overflows.
>>>>>
>>>>> Now, the main problem with the present patch is that it does not 
>>>>> fix one of those overflows. Namely, consider that "dwLength" is 
>>>>> very close to
>>>>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then aligning 
>>>>> it up to the next multiple of 8 will yield 0. In other words, "AlignedLength"
>>>>> will be zero.
>>>>>
>>>>> And when that happens, there's going to be an infinite loop just 
>>>>> the
>>>>> same: "OffSet" will not be zero, but it will be *stuck*. The
>>>>> SafeUint32Add() call at the bottom will succeed, but it will not 
>>>>> change the value of "OffSet".
>>>>>
>>>>> More at the bottom.
>>>>>
>>>>>
>>>>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) 
>>>>>> <= sizeof
>>>> (WIN_CERTIFICATE) ||
>>>>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) 
>>>>>> <
>>>> WinCertificate->dwLength) {
>>>>>>        break;
>>>>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
>>>>>>        }
>>>>>>        AuthData   = PkcsCertData->CertData;
>>>>>>        AuthDataSize = PkcsCertData->Hdr.dwLength - 
>>>>>> sizeof(PkcsCertData-
>>> Hdr);
>>>>>> +      IsAuthDataAssigned = TRUE;
>>>>>> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>      } else if (WinCertificate->wCertificateType ==
>> WIN_CERT_TYPE_EFI_GUID)
>>>> {
>>>>>>        //
>>>>>>        // The certificate is formatted as 
>>>>>> WIN_CERTIFICATE_UEFI_GUID which
>> is
>>>> described in UEFI Spec.
>>>>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
>>>>>>        if (WinCertUefiGuid->Hdr.dwLength <=
>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
>>>>>>          break;
>>>>>>        }
>>>>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid))
>> {
>>>>>> -        continue;
>>>>>> +      if (CompareGuid (&WinCertUefiGuid->CertType, 
>>>>>> + &gEfiCertPkcs7Guid))
>> {
>>>>>> +        AuthData = WinCertUefiGuid->CertData;
>>>>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>> +        IsAuthDataAssigned = TRUE;
>>>>>> +        HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>        }
>>>>>> -      AuthData = WinCertUefiGuid->CertData;
>>>>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>      } else {
>>>>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
>>>>>>          break;
>>>>>>        }
>>>>>> -      continue;
>>>>>>      }
>>>>>>
>>>>>> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>> -    if (EFI_ERROR (HashStatus)) {
>>>>>> -      continue;
>>>>>> -    }
>>>>>> -
>>>>>> -    //
>>>>>> -    // Check the digital signature against the revoked certificate in
>> forbidden
>>>> database (dbx).
>>>>>> -    //
>>>>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>> -      IsVerified = FALSE;
>>>>>> -      break;
>>>>>> -    }
>>>>>> -
>>>>>> -    //
>>>>>> -    // Check the digital signature against the valid certificate in allowed
>>>> database (db).
>>>>>> -    //
>>>>>> -    if (!IsVerified) {
>>>>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>> -        IsVerified = TRUE;
>>>>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
>>>>>> +      //
>>>>>> +      // Check the digital signature against the revoked 
>>>>>> + certificate in
>> forbidden
>>>> database (dbx).
>>>>>> +      //
>>>>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>> +        IsVerified = FALSE;
>>>>>> +        break;
>>>>>>        }
>>>>>> -    }
>>>>>>
>>>>>> -    //
>>>>>> -    // Check the image's hash value.
>>>>>> -    //
>>>>>> -    DbStatus = IsSignatureFoundInDatabase (
>>>>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
>>>>>> -                 mImageDigest,
>>>>>> -                 &mCertType,
>>>>>> -                 mImageDigestSize,
>>>>>> -                 &IsFound
>>>>>> -                 );
>>>>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
>> but %s
>>>> hash of image is found in DBX.\n", mHashTypeStr));
>>>>>> -      IsVerified = FALSE;
>>>>>> -      break;
>>>>>> -    }
>>>>>> +      //
>>>>>> +      // Check the digital signature against the valid 
>>>>>> + certificate in allowed
>>>> database (db).
>>>>>> +      //
>>>>>> +      if (!IsVerified) {
>>>>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>> +          IsVerified = TRUE;
>>>>>> +        }
>>>>>> +      }
>>>>>>
>>>>>> -    if (!IsVerified) {
>>>>>> +      //
>>>>>> +      // Check the image's hash value.
>>>>>> +      //
>>>>>>        DbStatus = IsSignatureFoundInDatabase (
>>>>>> -                   EFI_IMAGE_SECURITY_DATABASE,
>>>>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>                     mImageDigest,
>>>>>>                     &mCertType,
>>>>>>                     mImageDigestSize,
>>>>>>                     &IsFound
>>>>>>                     );
>>>>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>> -        IsVerified = TRUE;
>>>>>> -      } else {
>>>>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
>> but
>>>> signature is not allowed by DB and %s hash of image is not found in
>> DB/DBX.\n",
>>>> mHashTypeStr));
>>>>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is 
>>>>>> + signed
>>>> but %s hash of image is found in DBX.\n", mHashTypeStr));
>>>>>> +        IsVerified = FALSE;
>>>>>> +        break;
>>>>>>        }
>>>>>> +
>>>>>> +      if (!IsVerified) {
>>>>>> +        DbStatus = IsSignatureFoundInDatabase (
>>>>>> +                     EFI_IMAGE_SECURITY_DATABASE,
>>>>>> +                     mImageDigest,
>>>>>> +                     &mCertType,
>>>>>> +                     mImageDigestSize,
>>>>>> +                     &IsFound
>>>>>> +                     );
>>>>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>> +          IsVerified = TRUE;
>>>>>> +        } else {
>>>>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is 
>>>>>> + signed
>> but
>>>> signature is not allowed by DB and %s hash of image is not found in
>> DB/DBX.\n",
>>>> mHashTypeStr));
>>>>>> +        }
>>>>>> +      }
>>>>>> +    }
>>>>>> +
>>>>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
>>>>>> +    if (EFI_ERROR (AddStatus)) {
>>>>>> +      break;
>>>>>>      }
>>>>>> +    OffSet = Result;
>>>>>>    }
>>>>>>
>>>>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) 
>>>>>> {
>>>>>>
>>>>>
>>>>> There are other (smaller) reasons why I dislike this patch:
>>>>>
>>>>> - The "IsAuthDataAssigned" variable is superfluous; we could use 
>>>>> the existent "AuthData" variable (with a NULL-check and a 
>>>>> NULL-assignment) similarly.
>>>>>
>>>>> - The patch complicates / reorganizes the control flow needlessly. 
>>>>> This complication originates from placing the checked "OffSet" 
>>>>> increment at the bottom of the loop, which then requires the 
>>>>> removal of all the "continue" statements. But we don't need to 
>>>>> check-and-increment at the bottom. We can keep the increment 
>>>>> inside the "for" statement, only extend the *existent* room check 
>>>>> (which I've quoted) to take the alignment into account as well. If 
>>>>> there is enough room for the alignment in the security data 
>>>>> directory, then that guarantees there won't be a UINT32 overflow either.
>>>>>
>>>>> All in all, I'm proposing the following three patches instead. The 
>>>>> first two patches are preparation, the last patch is the fix.
>>>>>
>>>>> Patch#1:
>>>>>
>>>>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep 17
>> 00:00:00
>>>> 2001
>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
>>>>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib: extract  
>>>>>> SecDataDirEnd, SecDataDirLeft
>>>>>>
>>>>>> The following two quantities:
>>>>>>
>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
>>>>>>
>>>>>> are used multiple times in DxeImageVerificationHandler(). 
>>>>>> Introduce helper variables for them: "SecDataDirEnd" and "SecDataDirLeft", respectively.
>>>>>> This saves us multiple calculations and significantly simplifies the code.
>>>>>>
>>>>>> Note that all three summands above have type UINT32, therefore 
>>>>>> the new variables are also of type UINT32.
>>>>>>
>>>>>> This patch does not change behavior.
>>>>>>
>>>>>> (Note that the code already handles the case when the
>>>>>>
>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>
>>>>>> UINT32 addition overflows -- namely, in that case, the 
>>>>>> certificate loop is never entered, and the corruption check right 
>>>>>> after the loop fires.)
>>>>>>
>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>> ---
>>>>>>  
>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.c |
>> 12
>>>> ++++++++----
>>>>>>  1 file changed, 8 insertions(+), 4 deletions(-)
>>>>>>
>>>>>> diff --git
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.c 
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.c
>>>>>> index 36b87e16d53d..8761980c88aa 100644
>>>>>> ---
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>> .c
>>>>>> +++
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.c
>>>>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
>>>>>>    UINT8                                *AuthData;
>>>>>>    UINTN                                AuthDataSize;
>>>>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
>>>>>> +  UINT32                               SecDataDirEnd;
>>>>>> +  UINT32                               SecDataDirLeft;
>>>>>>    UINT32                               OffSet;
>>>>>>    CHAR16                               *NameStr;
>>>>>>    RETURN_STATUS                        PeCoffStatus;
>>>>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
>>>>>>    // "Attribute Certificate Table".
>>>>>>    // The first certificate starts at offset 
>>>>>> (SecDataDir->VirtualAddress) from
>> the
>>>> start of the file.
>>>>>>    //
>>>>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>> +       OffSet < SecDataDirEnd;
>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE 
>>>>>> (WinCertificate-
>>>>> dwLength))) {
>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>>>> (WIN_CERTIFICATE) ||
>>>>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>> WinCertificate->dwLength) {
>>>>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>> +        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>        break;
>>>>>>      }
>>>>>>
>>>>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
>>>>>>      }
>>>>>>    }
>>>>>>
>>>>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) 
>>>>>> {
>>>>>> +  if (OffSet != SecDataDirEnd) {
>>>>>>      //
>>>>>>      // The Size in Certificate Table or the attribute 
>>>>>> certificate table is
>> corrupted.
>>>>>>      //
>>>>>> --
>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>
>>>>>
>>>>> Patch#2:
>>>>>
>>>>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep 17 00:00:00
>>>> 2001
>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
>>>>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib: assign  
>>>>>> WinCertificate after size check
>>>>>>
>>>>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) check 
>>>>>> only guards the de-referencing of the "WinCertificate" pointer. 
>>>>>> It does not guard the calculation of hte pointer itself:
>>>>>>
>>>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>
>>>>>> This is wrong; if we don't know for sure that we have enough room 
>>>>>> for a WIN_CERTIFICATE, then even creating such a pointer, not 
>>>>>> just de-referencing it, may invoke undefined behavior.
>>>>>>
>>>>>> Move the pointer calculation after the size check.
>>>>>>
>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>> ---
>>>>>>  
>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.c |
>> 8
>>>> +++++---
>>>>>>  1 file changed, 5 insertions(+), 3 deletions(-)
>>>>>>
>>>>>> diff --git
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.c 
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.c
>>>>>> index 8761980c88aa..461ed7cfb5ac 100644
>>>>>> ---
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>> .c
>>>>>> +++
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.c
>>>>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>         OffSet < SecDataDirEnd;
>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE 
>>>>>> (WinCertificate-
>>>>> dwLength))) {
>>>>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>> -        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
>>>>>> +      break;
>>>>>> +    }
>>>>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>        break;
>>>>>>      }
>>>>>>
>>>>>> --
>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>
>>>>>
>>>>> Patch#3:
>>>>>
>>>>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17 00:00:00
>>>> 2001
>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
>>>>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch
>> alignment
>>>>>>  overflow (CVE-2019-14562)
>>>>>>
>>>>>> The DxeImageVerificationHandler() function currently checks 
>>>>>> whether "SecDataDir" has enough room for 
>>>>>> "WinCertificate->dwLength". However,
>>>> for
>>>>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to the 
>>>>>> next multiple of 8. If "WinCertificate->dwLength" is large 
>>>>>> enough, the alignment will return 0, and "OffSet" will be stuck at the same value.
>>>>>>
>>>>>> Check whether "SecDataDir" has room left for both 
>>>>>> "WinCertificate->dwLength" and the alignment.
>>>>>>
>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>> ---
>>>>>>  
>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.c |
>> 4
>>>> +++-
>>>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>>>>
>>>>>> diff --git
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.c 
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.c
>>>>>> index 461ed7cfb5ac..e38eb981b7a0 100644
>>>>>> ---
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>> .c
>>>>>> +++
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>> ib.c
>>>>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
>>>>>>        break;
>>>>>>      }
>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
>>>>>> +        (SecDataDirLeft - WinCertificate->dwLength <
>>>>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
>>>>>>        break;
>>>>>>      }
>>>>>>
>>>>>> --
>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>
>>>>>
>>>>> If Wenyi and the reviewers are OK with these patches, I can submit 
>>>>> them as a standalone patch series.
>>>>>
>>>>> Note that I do not have any reproducer for the issue; the best 
>>>>> testing that I could offer would be some light-weight Secure Boot 
>>>>> regression tests.
>>>>>
>>>>> Thanks
>>>>> Laszlo
>>>>>
>>>>>
>>>>> .
>>>>>
>>>>
>>>>
>>>>
>>>
>>
>>
>> 
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-18 13:12               ` Yao, Jiewen
@ 2020-08-18 17:29                 ` Bret Barkelew
  2020-08-18 23:00                   ` Yao, Jiewen
  2020-08-19  9:33                   ` Laszlo Ersek
  0 siblings, 2 replies; 32+ messages in thread
From: Bret Barkelew @ 2020-08-18 17:29 UTC (permalink / raw)
  To: devel@edk2.groups.io, Yao, Jiewen, lersek@redhat.com
  Cc: xiewenyi2@huawei.com, Wang, Jian J, huangming23@huawei.com,
	songdongkuang@huawei.com, Marvin Häuser, Vitaly Cheptsov

[-- Attachment #1: Type: text/plain, Size: 16224 bytes --]

Jiewen,

I don’t completely agree with your second point. If the underlying issue is already out of embargo, and we’ve just failed to fix it, that’s not a new issue. I would want to see fixes to the fixes fast-tracked (or at least heavily prioritized), rather than re-entering a full embargo period.

- Bret

From: Yao, Jiewen via groups.io<mailto:jiewen.yao=intel.com@groups.io>
Sent: Tuesday, August 18, 2020 6:12 AM
To: devel@edk2.groups.io<mailto:devel@edk2.groups.io>; lersek@redhat.com<mailto:lersek@redhat.com>
Cc: xiewenyi2@huawei.com<mailto:xiewenyi2@huawei.com>; Wang, Jian J<mailto:jian.j.wang@intel.com>; huangming23@huawei.com<mailto:huangming23@huawei.com>; songdongkuang@huawei.com<mailto:songdongkuang@huawei.com>; Marvin Häuser<mailto:mhaeuser@outlook.de>; Vitaly Cheptsov<mailto:vit9696@protonmail.com>
Subject: [EXTERNAL] Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset

Hi Laszlo
I agree with on your most points.
It is all about *return of investment* or *risk control*. Like we cannot pursue 100% security because we will bankrupt ourselves if so.

Here I just raise my concern.
1) If we alway allow developers’ low quality patch without test, the overall quality will become lower and lower. Personally I have no confidence to help catch all those issues. You are the role model on code review. But not all people review the code like you. We need both expertise and time for code review.

2) I purposely separate security fix from non security one, because the process is different. The embargo might be 6 months. What if we found the security patch does not fix the problem after embargo? Unlike we send one more patch tomorrow, we need wait for another 6 months...

thank you!
Yao, Jiewen


> 在 2020年8月18日,下午6:17,Laszlo Ersek <lersek@redhat.com> 写道:
>
> Hi Jiewen,
>
> (+Marvin, +Vitaly)
>
> On 08/18/20 01:23, Yao, Jiewen wrote:
>>> -----Original Message-----
>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Laszlo
>>> Ersek
>>> Sent: Tuesday, August 18, 2020 12:53 AM
>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
>>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>
> [...]
>
>> However, I do think the producer is mandatory for a fix or at least a
>> security fix.
>> The owner to fix the issue should guarantee the patch is good.
>> The owner shall never rely on the code reviewer to figure out if the
>> patch is good and complete.
>>
>> I have some bad experience that bug owner just wrote a patch and tried
>> to fix a problem, without any test.
>> And it happened passed code review from someone who does not well
>> understand the problem, but give rb based upon the time pressure.
>> Later, the fix was approved to be useless.
>>
>> In my memory, at least 3 cases were security fix. They are found, just
>> because they are sensitive, more people took a look later.
>>    It was simple. It was one-line change.
>>   But it has not test, and it was wrong.
>> "It was ridiculous" -- commented by the people who find the so-called
>> security fix does not fix the issue.
>
> Just because sloppy/rushed reviews exist, and just because reviewers
> operate under time pressure, we should not automatically reject security
> fixes that come without a reproducer.
>
> Some organizations do develop reproducers, but they never share them
> publicly (for fear of abuse by others).
>
> But more importantly, in an open development project, a developer could
> have time and expertise to contribute a fix, but not to create a
> reproducer.
>
> - If we make contributing harder, fewer people will upstream their
>  fixes.
>
> - If we make contributing harder, then contributions that do make it to
>  the tree will be of higher quality.
>
> Both statements ring true to me -- so it's a tradeoff.
>
> (By "we", I mean the edk2 community.)
>
>>> Additionally, the exact statement that the bug report does make,
>>> namely
>>>
>>>  it's possible to overflow Offset back to 0 causing an endless loop
>>>
>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can
>>> be overflowed to zero, but the *addend* that is added to OffSet can
>>> be overflowed to zero. Therefore the infinite loop will arise because
>>> OffSet remains stuck at its present value, and not because OffSet
>>> will be re-set to zero.
>>>
>>> For the reasons, we can only speculate as to what the actual problem
>>> is, unless Jian decides to join the discussion and clarifies what he
>>> had in mind originally.
>>
>> [Jiewen] Would you please clarify what do you mean "we" here?
>> If "we" means the bug dispatcher, it is totally OK. The dispatcher
>> just assign the bug.
>> If "we" means the developer assigned to fix the bug, it is NOT OK. The
>> developer should take the responsibility to understand the problem.
>
> By "we", I mean the edk2 community.
>
>>> We can write a patch based on code analysis. It's possible to
>>> identify integer overflows based on code analysis, and it's possible
>>> to verify the correctness of fixes by code review. Obviously testing
>>> is always good, but many times, constructing reproducers for such
>>> issues that were found by code review, is difficult and time
>>> consuming. We can say that we don't fix vulnerabilities without
>>> reproducers, or we can say that we make an effort to fix them even if
>>> all we have is code analysis (and not a reproducer).
>>
>> [Jiewen] I would say: yes and no.
>> Yes, I agree with you that it might be difficult and time consuming to
>> construct the reproducer.
>> However, "obviously" is a subject term. Someone may think something is
>> obvious, but other people does not.
>> We should be clear the responsibility of the patch provider is to
>> provide high quality patch.
>> Having basic unit test is the best way to prove that the fix is good.
>>
>> I have seen bad cases when I ask for the test for patch, then the
>> answer I got is: "I test the windows boot".
>> But the test - windows boot - has nothing related to the patch. It
>> only proves no regression, but cannot prove the issue described is
>> resolved.
>
> Right. It would be ideal if every patch came with a unit test. But that
> also means some folks will contribute less.
>
> Consider normal (not security) patches. We require that all function
> return values be checked (unless it really doesn't matter if a function
> call fails). If a function call fails, we need to roll back the actions
> taken thus far. Release resources and so on. This is why we have the
> "cascade of error handling labels" pattern.
>
> But, of course, we don't test every possible error path in the code. So
> what's the solution there:
>
> - reject such patches that carefully construct the error paths, but do
>  not provide unit tests with complete error path coverage?
>
> - say that we don't care about thorough error paths, so let's just hang,
>  or leak resources, whenever something fails?
>
> Personally I prefer the detailed error paths. They need to be written
> and reviewed carefully. And they can be accepted even if they are not
> tested with complete coverage.
>
> Some people think otherwise; they say no untested (untestable) code
> should ever be merged.
>
> Back to security patches -- creating reproducers usually requires a
> setup (tools, expertise, time allocation etc) that is different from a
> "normal" setup. It may require specialized binary format editors,
> expertise in "penetration testing", and so on.
>
> I mostly know the C language rules that pertain to integer and buffer
> overflows, so I can usually spot their violations in C code, and propose
> fixes for them too. But I'm not a security researcher, so I don't write
> exploits as a norm -- I don't even investigate, generally speaking, the
> potential practical impact of "undefined behavior". When there's a
> buffer overflow or integer overflow in the code, that's the *end* of the
> story for me, while it's the *start* of the work for a security
> researcher.
>
> When you require reproducers for all security patches, you restrict the
> potential contributor pool to security researchers.
>
>> Let's think again in this case, if the patch provider does some basic
>> unit test, he/she may find out the problem by himself/herself.
>> That can save other people's time to review.
>>
>> I don't prefer to move the responsibility from patch provider to the
>> code reviewer to check if the fix is good.
>> Otherwise, the code reviewer may be overwhelmed.
>>
>> We may clarify and document the role and responsibility in EDKII
>> clearly. Once that is ready, we can follow the rule.
>> Before that is ready, in this particular case, I still prefer we have
>> producer to prove the patch is good.
>
> OK, thanks for explaining.
>
> Given that I'm unable to create such a PE file (from scratch or by
> modifying another one), I won't post the patches stand-alone.
>
>>> So the above paragraph concerns "correctness". Regarding
>>> "completeness", I guarantee you that this patch does not fix *all*
>>> problems related to PE parsing. (See the other BZ tickets.) It does
>>> fix *one* issue with PE parsing. We can say that we try to fix such
>>> issues gradually (give different CVE numbers to different issues, and
>>> address them one at a time), or we can say that we rewrite PE parsing
>>> from the ground up. (BTW: I have seriously attempted that in the
>>> past, and I gave up, because the PE format is FUBAR.)
>>
>> [Jiewen] Maybe there is misunderstanding.
>> I do not mean to let patch provider to fix all issue in PE parsing.
>> Just like we cannot file one Bugzilla to fix all issue in EDKII - it
>> is unfair.
>>
>> What I mean is that the patch provider should guarantee the
>> correctness and completeness of the issue in the bug report.
>>
>> One faked bad example of correctness is:
>>    A bug report is file to say: the code has overflow class A.
>>    The factor is: the code has overflow class A at line X and line Y.
>>    The patch only modified some code at line X, but the overflow
>>    class A at line X still exists.
>>
>> One faked bad example of completeness is:
>>    A bug report is file to say: the code has overflow class A.
>>    The factor is: the code has overflow class A at line X and line Y.
>>    The patch only fixed the overflow class A at line X but not line
>>    Y.
>>
>> The patch provider should take responsibility to do that work
>> seriously to find out issue in line X and line Y and fix them.
>> He/she may choose to just fix line X and line Y. Rewrite is whole
>> module is NOT required.
>
> I agree completely.
>
> My point was that we need the bug report to be precise, in the first
> place. If the bug report doesn't clearly identify lines X and Y, we will
> likely not get the completeness part right.
>
> "Clearly identify" may mean spelling out lines X and Y specifically. Or
> it may mean defining "class A" sufficiently clearly that someone else
> reading the affected function can find X and Y themselves.
>
>> If I can give some comment, I would think about the provide the fix in
>> BasePeCoffLib.
>
> From a software design perspective, you are 100% right.
>
> Unfortunately, I can't do it.
>
> That's what I mentioned before -- I had tried rewriting BasePeCoffLib,
> because in my opinion, BasePeCoffLib is unsalvageable in its current
> form. And I gave up on the rewrite.
>
> Please see the following email. I sent it to some people off-list, on
> 2020-Feb-14:
>
>> There are currently four (4) TianoCore security BZs (1957, 1990, 1993,
>> 2215), embargoed, that describe various ways in which cunningly
>> crafted PE images can evade Secure Boot verification.
>>
>> [...]
>>
>> Primarily, I just couldn't find my peace with the idea that fixing
>> such PE/COFF parsing mistakes (integer overflows, buffer overflows)
>> *must* be a one-by-one fixing game. I wanted an approach that would
>> fix these *classes* of vulnerabilities, in PE/COFF parsing.
>>
>> So, beginnning of this February I returned to this topic, and spent
>> two days on prototyping / researching a container / interval based
>> approach. Here's one of the commit messages, as a way of explaining:
>>
>>    OvmfPkg/DxePeCoffValidatorLib: introduce CONTAINER type and helper funcs
>>
>>    For validating the well-formedness of a PE/COFF file, introduce the
>>    CONTAINER type, and some workhorse functions. (The functions added in this
>>    patch will not be called directly from the code that will process PE/COFF
>>    structures.)
>>
>>    The CONTAINER type describes a contiguous non-empty interval in a PE/COFF
>>    file (on-disk representation, or in-memory representation). Containers can
>>    be nested. The data from scalar-sized containers can be read out, as part
>>    of their creation. For on-disk representations of PE/COFF files, scalar
>>    reads are permitted; for in-memory representations, no data access is
>>    permitted (only CONTAINER tracking / nesting).
>>
>>    The goals of CONTAINER are the following:
>>
>>    - enforce the proper nesting of PE/COFF structures (structure boundaries
>>      must not be crossed by runs of data);
>>
>>    - prevent integer overflows and buffer overflows;
>>
>>    - prevent zero-size structures;
>>
>>    - prevent infinite nesting by requiring proper sub-intervals;
>>
>>    - prevent internal PE/COFF pointers from aliasing each other (unless they
>>      point at container and containee structures);
>>
>>    - terminate nesting at scalar-sized containers;
>>
>>    - assuming an array of pointers is processed in increasing element order,
>>      enforce that the pointed-to objects are located at increasing offsets
>>      too;
>>
>>    - assign human-readable names to PE/COFF structures and fields, for
>>      debugging PE/COFF malformations.
>>
>> Because, several of the vulnerabilities exploited cross-directed and
>> aliased internal pointers in PE/COFF files.
>>
>> Two days of delirious spec reading and coding later, and 2000+ lines
>> later, I decided that my idea was unviable. The PE/COFF spec was so
>> incredibly mis-designed and crufty that enforcing the *internal*
>> consistency of all the size fields and the internal pointers would
>> inevitably fall into one of the following categories:
>>
>> - the checks wouldn't be strict enough, and some nasty images would
>>  slip through,
>>
>> - the checks would be too strict, and some quirky, but valid, images
>>  would be unjustifiedly caught.
>>
>> So I gave up and I've accepted that it remains a whack-a-mole game.
>> [...]
>>
>> (NB: I don't claim that ELF is not similarly brain-damaged.)
>
> So now, I've only considered contributing patches for bug#2215 because
> the code in question resides in DxeImageVerificationLib, and *not* in
> BasePeCoffLib. I'm not going to touch BasePeCoffLib -- in my opinion,
> BasePeCoffLib is unfixable without a complete rewrite.
>
> I would *like* if BasePeCoffLib were fixable incrementally, but I just
> don't see how that's possible.
>
> In support of my opinion, please open the following bugzilla ticket:
>
>  https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2643&amp;data=02%7C01%7Cbret.barkelew%40microsoft.com%7Cada52c41a3db41968b7408d843785e58%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637333531565237004&amp;sdata=C59ZmrHEONxeui4lkIo1RFTTLUOEuU8OQXUbWoS9JWc%3D&amp;reserved=0
>
> and search the comments (with the browser's in-page search feature, such
> as Ctrl+F) for the following expression:
>
>  new PE loader
>
> I understand exactly what Vitaly and Marvin meant in those comments. :(
>
> Thanks,
> Laszlo
>
>
>
>




[-- Attachment #2: Type: text/html, Size: 23067 bytes --]

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-18 17:29                 ` Bret Barkelew
@ 2020-08-18 23:00                   ` Yao, Jiewen
  2020-08-19  9:33                   ` Laszlo Ersek
  1 sibling, 0 replies; 32+ messages in thread
From: Yao, Jiewen @ 2020-08-18 23:00 UTC (permalink / raw)
  To: Bret Barkelew, devel@edk2.groups.io, lersek@redhat.com
  Cc: xiewenyi2@huawei.com, Wang, Jian J, huangming23@huawei.com,
	songdongkuang@huawei.com, Marvin Häuser, Vitaly Cheptsov

[-- Attachment #1: Type: text/plain, Size: 17148 bytes --]

OK. The second point is my personal understanding on embargo process.
I might be wrong. Maybe we should let infosec team to clarify the process.

Thank you
Yao Jiewen

From: Bret Barkelew <Bret.Barkelew@microsoft.com>
Sent: Wednesday, August 19, 2020 1:29 AM
To: devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>; lersek@redhat.com
Cc: xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>; huangming23@huawei.com; songdongkuang@huawei.com; Marvin Häuser <mhaeuser@outlook.de>; Vitaly Cheptsov <vit9696@protonmail.com>
Subject: RE: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset

Jiewen,

I don’t completely agree with your second point. If the underlying issue is already out of embargo, and we’ve just failed to fix it, that’s not a new issue. I would want to see fixes to the fixes fast-tracked (or at least heavily prioritized), rather than re-entering a full embargo period.

- Bret

From: Yao, Jiewen via groups.io<mailto:jiewen.yao=intel.com@groups.io>
Sent: Tuesday, August 18, 2020 6:12 AM
To: devel@edk2.groups.io<mailto:devel@edk2.groups.io>; lersek@redhat.com<mailto:lersek@redhat.com>
Cc: xiewenyi2@huawei.com<mailto:xiewenyi2@huawei.com>; Wang, Jian J<mailto:jian.j.wang@intel.com>; huangming23@huawei.com<mailto:huangming23@huawei.com>; songdongkuang@huawei.com<mailto:songdongkuang@huawei.com>; Marvin Häuser<mailto:mhaeuser@outlook.de>; Vitaly Cheptsov<mailto:vit9696@protonmail.com>
Subject: [EXTERNAL] Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset

Hi Laszlo
I agree with on your most points.
It is all about *return of investment* or *risk control*. Like we cannot pursue 100% security because we will bankrupt ourselves if so.

Here I just raise my concern.
1) If we alway allow developers’ low quality patch without test, the overall quality will become lower and lower. Personally I have no confidence to help catch all those issues. You are the role model on code review. But not all people review the code like you. We need both expertise and time for code review.

2) I purposely separate security fix from non security one, because the process is different. The embargo might be 6 months. What if we found the security patch does not fix the problem after embargo? Unlike we send one more patch tomorrow, we need wait for another 6 months...

thank you!
Yao, Jiewen


> 在 2020年8月18日,下午6:17,Laszlo Ersek <lersek@redhat.com<mailto:lersek@redhat.com>> 写道:
>
> Hi Jiewen,
>
> (+Marvin, +Vitaly)
>
> On 08/18/20 01:23, Yao, Jiewen wrote:
>>> -----Original Message-----
>>> From: devel@edk2.groups.io<mailto:devel@edk2.groups.io> <devel@edk2.groups.io<mailto:devel@edk2.groups.io>> On Behalf Of Laszlo
>>> Ersek
>>> Sent: Tuesday, August 18, 2020 12:53 AM
>>> To: Yao, Jiewen <jiewen.yao@intel.com<mailto:jiewen.yao@intel.com>>; devel@edk2.groups.io<mailto:devel@edk2.groups.io>;
>>> xiewenyi2@huawei.com<mailto:xiewenyi2@huawei.com>; Wang, Jian J <jian.j.wang@intel.com<mailto:jian.j.wang@intel.com>>
>>> Cc: huangming23@huawei.com<mailto:huangming23@huawei.com>; songdongkuang@huawei.com<mailto:songdongkuang@huawei.com>
>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>
> [...]
>
>> However, I do think the producer is mandatory for a fix or at least a
>> security fix.
>> The owner to fix the issue should guarantee the patch is good.
>> The owner shall never rely on the code reviewer to figure out if the
>> patch is good and complete.
>>
>> I have some bad experience that bug owner just wrote a patch and tried
>> to fix a problem, without any test.
>> And it happened passed code review from someone who does not well
>> understand the problem, but give rb based upon the time pressure.
>> Later, the fix was approved to be useless.
>>
>> In my memory, at least 3 cases were security fix. They are found, just
>> because they are sensitive, more people took a look later.
>>    It was simple. It was one-line change.
>>   But it has not test, and it was wrong.
>> "It was ridiculous" -- commented by the people who find the so-called
>> security fix does not fix the issue.
>
> Just because sloppy/rushed reviews exist, and just because reviewers
> operate under time pressure, we should not automatically reject security
> fixes that come without a reproducer.
>
> Some organizations do develop reproducers, but they never share them
> publicly (for fear of abuse by others).
>
> But more importantly, in an open development project, a developer could
> have time and expertise to contribute a fix, but not to create a
> reproducer.
>
> - If we make contributing harder, fewer people will upstream their
>  fixes.
>
> - If we make contributing harder, then contributions that do make it to
>  the tree will be of higher quality.
>
> Both statements ring true to me -- so it's a tradeoff.
>
> (By "we", I mean the edk2 community.)
>
>>> Additionally, the exact statement that the bug report does make,
>>> namely
>>>
>>>  it's possible to overflow Offset back to 0 causing an endless loop
>>>
>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can
>>> be overflowed to zero, but the *addend* that is added to OffSet can
>>> be overflowed to zero. Therefore the infinite loop will arise because
>>> OffSet remains stuck at its present value, and not because OffSet
>>> will be re-set to zero.
>>>
>>> For the reasons, we can only speculate as to what the actual problem
>>> is, unless Jian decides to join the discussion and clarifies what he
>>> had in mind originally.
>>
>> [Jiewen] Would you please clarify what do you mean "we" here?
>> If "we" means the bug dispatcher, it is totally OK. The dispatcher
>> just assign the bug.
>> If "we" means the developer assigned to fix the bug, it is NOT OK. The
>> developer should take the responsibility to understand the problem.
>
> By "we", I mean the edk2 community.
>
>>> We can write a patch based on code analysis. It's possible to
>>> identify integer overflows based on code analysis, and it's possible
>>> to verify the correctness of fixes by code review. Obviously testing
>>> is always good, but many times, constructing reproducers for such
>>> issues that were found by code review, is difficult and time
>>> consuming. We can say that we don't fix vulnerabilities without
>>> reproducers, or we can say that we make an effort to fix them even if
>>> all we have is code analysis (and not a reproducer).
>>
>> [Jiewen] I would say: yes and no.
>> Yes, I agree with you that it might be difficult and time consuming to
>> construct the reproducer.
>> However, "obviously" is a subject term. Someone may think something is
>> obvious, but other people does not.
>> We should be clear the responsibility of the patch provider is to
>> provide high quality patch.
>> Having basic unit test is the best way to prove that the fix is good.
>>
>> I have seen bad cases when I ask for the test for patch, then the
>> answer I got is: "I test the windows boot".
>> But the test - windows boot - has nothing related to the patch. It
>> only proves no regression, but cannot prove the issue described is
>> resolved.
>
> Right. It would be ideal if every patch came with a unit test. But that
> also means some folks will contribute less.
>
> Consider normal (not security) patches. We require that all function
> return values be checked (unless it really doesn't matter if a function
> call fails). If a function call fails, we need to roll back the actions
> taken thus far. Release resources and so on. This is why we have the
> "cascade of error handling labels" pattern.
>
> But, of course, we don't test every possible error path in the code. So
> what's the solution there:
>
> - reject such patches that carefully construct the error paths, but do
>  not provide unit tests with complete error path coverage?
>
> - say that we don't care about thorough error paths, so let's just hang,
>  or leak resources, whenever something fails?
>
> Personally I prefer the detailed error paths. They need to be written
> and reviewed carefully. And they can be accepted even if they are not
> tested with complete coverage.
>
> Some people think otherwise; they say no untested (untestable) code
> should ever be merged.
>
> Back to security patches -- creating reproducers usually requires a
> setup (tools, expertise, time allocation etc) that is different from a
> "normal" setup. It may require specialized binary format editors,
> expertise in "penetration testing", and so on.
>
> I mostly know the C language rules that pertain to integer and buffer
> overflows, so I can usually spot their violations in C code, and propose
> fixes for them too. But I'm not a security researcher, so I don't write
> exploits as a norm -- I don't even investigate, generally speaking, the
> potential practical impact of "undefined behavior". When there's a
> buffer overflow or integer overflow in the code, that's the *end* of the
> story for me, while it's the *start* of the work for a security
> researcher.
>
> When you require reproducers for all security patches, you restrict the
> potential contributor pool to security researchers.
>
>> Let's think again in this case, if the patch provider does some basic
>> unit test, he/she may find out the problem by himself/herself.
>> That can save other people's time to review.
>>
>> I don't prefer to move the responsibility from patch provider to the
>> code reviewer to check if the fix is good.
>> Otherwise, the code reviewer may be overwhelmed.
>>
>> We may clarify and document the role and responsibility in EDKII
>> clearly. Once that is ready, we can follow the rule.
>> Before that is ready, in this particular case, I still prefer we have
>> producer to prove the patch is good.
>
> OK, thanks for explaining.
>
> Given that I'm unable to create such a PE file (from scratch or by
> modifying another one), I won't post the patches stand-alone.
>
>>> So the above paragraph concerns "correctness". Regarding
>>> "completeness", I guarantee you that this patch does not fix *all*
>>> problems related to PE parsing. (See the other BZ tickets.) It does
>>> fix *one* issue with PE parsing. We can say that we try to fix such
>>> issues gradually (give different CVE numbers to different issues, and
>>> address them one at a time), or we can say that we rewrite PE parsing
>>> from the ground up. (BTW: I have seriously attempted that in the
>>> past, and I gave up, because the PE format is FUBAR.)
>>
>> [Jiewen] Maybe there is misunderstanding.
>> I do not mean to let patch provider to fix all issue in PE parsing.
>> Just like we cannot file one Bugzilla to fix all issue in EDKII - it
>> is unfair.
>>
>> What I mean is that the patch provider should guarantee the
>> correctness and completeness of the issue in the bug report.
>>
>> One faked bad example of correctness is:
>>    A bug report is file to say: the code has overflow class A.
>>    The factor is: the code has overflow class A at line X and line Y.
>>    The patch only modified some code at line X, but the overflow
>>    class A at line X still exists.
>>
>> One faked bad example of completeness is:
>>    A bug report is file to say: the code has overflow class A.
>>    The factor is: the code has overflow class A at line X and line Y.
>>    The patch only fixed the overflow class A at line X but not line
>>    Y.
>>
>> The patch provider should take responsibility to do that work
>> seriously to find out issue in line X and line Y and fix them.
>> He/she may choose to just fix line X and line Y. Rewrite is whole
>> module is NOT required.
>
> I agree completely.
>
> My point was that we need the bug report to be precise, in the first
> place. If the bug report doesn't clearly identify lines X and Y, we will
> likely not get the completeness part right.
>
> "Clearly identify" may mean spelling out lines X and Y specifically. Or
> it may mean defining "class A" sufficiently clearly that someone else
> reading the affected function can find X and Y themselves.
>
>> If I can give some comment, I would think about the provide the fix in
>> BasePeCoffLib.
>
> From a software design perspective, you are 100% right.
>
> Unfortunately, I can't do it.
>
> That's what I mentioned before -- I had tried rewriting BasePeCoffLib,
> because in my opinion, BasePeCoffLib is unsalvageable in its current
> form. And I gave up on the rewrite.
>
> Please see the following email. I sent it to some people off-list, on
> 2020-Feb-14:
>
>> There are currently four (4) TianoCore security BZs (1957, 1990, 1993,
>> 2215), embargoed, that describe various ways in which cunningly
>> crafted PE images can evade Secure Boot verification.
>>
>> [...]
>>
>> Primarily, I just couldn't find my peace with the idea that fixing
>> such PE/COFF parsing mistakes (integer overflows, buffer overflows)
>> *must* be a one-by-one fixing game. I wanted an approach that would
>> fix these *classes* of vulnerabilities, in PE/COFF parsing.
>>
>> So, beginnning of this February I returned to this topic, and spent
>> two days on prototyping / researching a container / interval based
>> approach. Here's one of the commit messages, as a way of explaining:
>>
>>    OvmfPkg/DxePeCoffValidatorLib: introduce CONTAINER type and helper funcs
>>
>>    For validating the well-formedness of a PE/COFF file, introduce the
>>    CONTAINER type, and some workhorse functions. (The functions added in this
>>    patch will not be called directly from the code that will process PE/COFF
>>    structures.)
>>
>>    The CONTAINER type describes a contiguous non-empty interval in a PE/COFF
>>    file (on-disk representation, or in-memory representation). Containers can
>>    be nested. The data from scalar-sized containers can be read out, as part
>>    of their creation. For on-disk representations of PE/COFF files, scalar
>>    reads are permitted; for in-memory representations, no data access is
>>    permitted (only CONTAINER tracking / nesting).
>>
>>    The goals of CONTAINER are the following:
>>
>>    - enforce the proper nesting of PE/COFF structures (structure boundaries
>>      must not be crossed by runs of data);
>>
>>    - prevent integer overflows and buffer overflows;
>>
>>    - prevent zero-size structures;
>>
>>    - prevent infinite nesting by requiring proper sub-intervals;
>>
>>    - prevent internal PE/COFF pointers from aliasing each other (unless they
>>      point at container and containee structures);
>>
>>    - terminate nesting at scalar-sized containers;
>>
>>    - assuming an array of pointers is processed in increasing element order,
>>      enforce that the pointed-to objects are located at increasing offsets
>>      too;
>>
>>    - assign human-readable names to PE/COFF structures and fields, for
>>      debugging PE/COFF malformations.
>>
>> Because, several of the vulnerabilities exploited cross-directed and
>> aliased internal pointers in PE/COFF files.
>>
>> Two days of delirious spec reading and coding later, and 2000+ lines
>> later, I decided that my idea was unviable. The PE/COFF spec was so
>> incredibly mis-designed and crufty that enforcing the *internal*
>> consistency of all the size fields and the internal pointers would
>> inevitably fall into one of the following categories:
>>
>> - the checks wouldn't be strict enough, and some nasty images would
>>  slip through,
>>
>> - the checks would be too strict, and some quirky, but valid, images
>>  would be unjustifiedly caught.
>>
>> So I gave up and I've accepted that it remains a whack-a-mole game.
>> [...]
>>
>> (NB: I don't claim that ELF is not similarly brain-damaged.)
>
> So now, I've only considered contributing patches for bug#2215 because
> the code in question resides in DxeImageVerificationLib, and *not* in
> BasePeCoffLib. I'm not going to touch BasePeCoffLib -- in my opinion,
> BasePeCoffLib is unfixable without a complete rewrite.
>
> I would *like* if BasePeCoffLib were fixable incrementally, but I just
> don't see how that's possible.
>
> In support of my opinion, please open the following bugzilla ticket:
>
>  https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2643&amp;data=02%7C01%7Cbret.barkelew%40microsoft.com%7Cada52c41a3db41968b7408d843785e58%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637333531565237004&amp;sdata=C59ZmrHEONxeui4lkIo1RFTTLUOEuU8OQXUbWoS9JWc%3D&amp;reserved=0
>
> and search the comments (with the browser's in-page search feature, such
> as Ctrl+F) for the following expression:
>
>  new PE loader
>
> I understand exactly what Vitaly and Marvin meant in those comments. :(
>
> Thanks,
> Laszlo
>
>
>
>




[-- Attachment #2: Type: text/html, Size: 25099 bytes --]

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-18 15:18               ` john.mathews
@ 2020-08-19  9:26                 ` Laszlo Ersek
  2020-08-28  3:17                   ` wenyi,xie
  0 siblings, 1 reply; 32+ messages in thread
From: Laszlo Ersek @ 2020-08-19  9:26 UTC (permalink / raw)
  To: Mathews, John, Wang, Jian J, devel@edk2.groups.io, Yao, Jiewen,
	xiewenyi2@huawei.com
  Cc: huangming23@huawei.com, songdongkuang@huawei.com

On 08/18/20 17:18, Mathews, John wrote:
> I dug up the original report details.  This was noted as a concern during a source code inspection.  There was no demonstration of how it might be triggered.
> 
> " There is an integer overflow vulnerability in the DxeImageVerificationHandler function when
> parsing the PE files attribute certificate table. In cases where WinCertificate->dwLength is
> sufficiently large, it's possible to overflow Offset back to 0 causing an endless loop."
> 
> The recommendation was to add stricter checking of "Offset" and the embedded length fields of certificate data
> before using them.

Thanks for checking!

Laszlo

> 
> 
> 
> -----Original Message-----
> From: Laszlo Ersek <lersek@redhat.com> 
> Sent: Tuesday, August 18, 2020 1:59 AM
> To: Wang, Jian J <jian.j.wang@intel.com>; devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>; xiewenyi2@huawei.com
> Cc: huangming23@huawei.com; songdongkuang@huawei.com; Mathews, John <john.mathews@intel.com>
> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> 
> On 08/18/20 04:10, Wang, Jian J wrote:
>> Laszlo,
>>
>> My apologies for the slow response. I'm not the original reporter but 
>> just the BZ submitter. And I didn't do deep analysis to this issue. 
>> The issues was reported from one internal team. Add John in loop to see if he knows more about it or not.
>>
>> My superficial understanding on such issue is that, if there's 
>> "potential" issue in theory and hard to reproduce, it's still worthy 
>> of using an alternative way to replace the original implementation 
>> with no "potential" issue at all. Maybe we don't have to prove old way is something wrong but must prove that the new way is really safe.
> 
> I agree, thanks.
> 
> It would be nice to hear more from the internal team about the originally reported (even if hard-to-trigger) issue.
> 
> Thanks!
> Laszlo
> 
>>
>> Regards,
>> Jian
>>
>>> -----Original Message-----
>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Laszlo 
>>> Ersek
>>> Sent: Tuesday, August 18, 2020 12:53 AM
>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; 
>>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1] 
>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>
>>> Hi Jiewen,
>>>
>>> On 08/14/20 10:53, Yao, Jiewen wrote:
>>>>> To Jiewen,
>>>>> Sorry, I don't have environment to reproduce the issue.
>>>>
>>>> Please help me understand, if you don’t have environment to 
>>>> reproduce the
>>> issue, how do you guarantee that your patch does fix the problem and 
>>> we don’t have any other vulnerabilities?
>>>
>>> The original bug report in
>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is seriously 
>>> lacking. It does not go into detail about the alleged integer overflow.
>>> It does not quote the code, does not explain the control flow, does 
>>> not identify the exact edk2 commit at which the vulnerability exists.
>>>
>>> The bug report also does not offer a reproducer.
>>>
>>> Additionally, the exact statement that the bug report does make, 
>>> namely
>>>
>>>   it's possible to overflow Offset back to 0 causing an endless loop
>>>
>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can 
>>> be overflowed to zero, but the *addend* that is added to OffSet can 
>>> be overflowed to zero. Therefore the infinite loop will arise because 
>>> OffSet remains stuck at its present value, and not because OffSet 
>>> will be re-set to zero.
>>>
>>> For the reasons, we can only speculate as to what the actual problem 
>>> is, unless Jian decides to join the discussion and clarifies what he 
>>> had in mind originally.
>>>
>>> My understanding (or even "reconstruction") of the vulnerability is 
>>> described above, and in the patches that I proposed.
>>>
>>> We can write a patch based on code analysis. It's possible to 
>>> identify integer overflows based on code analysis, and it's possible 
>>> to verify the correctness of fixes by code review. Obviously testing 
>>> is always good, but many times, constructing reproducers for such 
>>> issues that were found by code review, is difficult and time 
>>> consuming. We can say that we don't fix vulnerabilities without 
>>> reproducers, or we can say that we make an effort to fix them even if 
>>> all we have is code analysis (and not a reproducer).
>>>
>>> So the above paragraph concerns "correctness". Regarding 
>>> "completeness", I guarantee you that this patch does not fix *all* 
>>> problems related to PE parsing. (See the other BZ tickets.) It does 
>>> fix *one* issue with PE parsing. We can say that we try to fix such 
>>> issues gradually (give different CVE numbers to different issues, and 
>>> address them one at a time), or we can say that we rewrite PE parsing from the ground up.
>>> (BTW: I have seriously attempted that in the past, and I gave up, 
>>> because the PE format is FUBAR.)
>>>
>>> In summary:
>>>
>>> - the problem statement is unclear,
>>>
>>> - it seems like there is indeed an integer overflow problem in the 
>>> SecDataDir parsing loop, but it's uncertain whether the bug reporter 
>>> had exactly that in mind
>>>
>>> - PE parsing is guaranteed to have other vulnerabilities elsewhere in 
>>> edk2, but I'm currently unaware of other such issues in 
>>> DxeImageVerificationLib specifically
>>>
>>> - even if there are other such problems (in DxeImageVerificationLib 
>>> or elswehere), fixing this bug that we know about is likely 
>>> worthwhile
>>>
>>> - for many such bugs, constructing a reproducer is difficult and time 
>>> consuming; code analysis, and *regression-testing* are frequently the 
>>> only tools we have. That doesn't mean we should ignore this class of bugs.
>>>
>>> (Fixing integer overflows retro-actively is more difficult than 
>>> writing overflow-free code in the first place, but that ship has 
>>> sailed; so we can only fight these bugs incrementally now, unless we 
>>> can rewrite PE parsing with a new data structure from the ground up. 
>>> Again I tried that and gave up, because the spec is not public, and 
>>> what I did manage to learn about PE, showed that it was insanely 
>>> over-engineered. I'm not saying that other binary / executable 
>>> formats are better, of course.)
>>>
>>> Please check out my patches (inlined elsewhere in this thread), and 
>>> comment whether you'd like me to post them to the list as a 
>>> standalone series.
>>>
>>> Jian: it wouldn't hurt if you commented as well.
>>>
>>> Thanks
>>> Laszlo
>>>
>>>>> -----Original Message-----
>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>>> wenyi,xie
>>>>> via groups.io
>>>>> Sent: Friday, August 14, 2020 3:54 PM
>>>>> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io; Yao, 
>>>>> Jiewen <jiewen.yao@intel.com>; Wang, Jian J <jian.j.wang@intel.com>
>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1] 
>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>
>>>>> To Laszlo,
>>>>> Thank you for your detailed description, I agree with what you 
>>>>> analyzed and
>>> I'm
>>>>> OK with your patches, it's
>>>>> correct and much simpler.
>>>>>
>>>>> To Jiewen,
>>>>> Sorry, I don't have environment to reproduce the issue.
>>>>>
>>>>> Thanks
>>>>> Wenyi
>>>>>
>>>>> On 2020/8/14 2:50, Laszlo Ersek wrote:
>>>>>> On 08/13/20 13:55, Wenyi Xie wrote:
>>>>>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
>>>>>>>
>>>>>>> There is an integer overflow vulnerability in 
>>>>>>> DxeImageVerificationHandler function when parsing the PE files 
>>>>>>> attribute certificate table. In cases where 
>>>>>>> WinCertificate->dwLength is sufficiently large, it's possible to overflow Offset back to 0 causing an endless loop.
>>>>>>>
>>>>>>> Check offset inbetween VirtualAddress and VirtualAddress + Size.
>>>>>>> Using SafeintLib to do offset addition with result check.
>>>>>>>
>>>>>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>>>>>>> Cc: Jian J Wang <jian.j.wang@intel.com>
>>>>>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>>>>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
>>>>>>> ---
>>>>>>>  
>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>> ib.inf
>>> |
>>>>> 1 +
>>>>>>>  
>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>> ib.h
>>> |
>>>>> 1 +
>>>>>>>  
>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>> ib.c
>>> |
>>>>> 111 +++++++++++---------
>>>>>>>  3 files changed, 63 insertions(+), 50 deletions(-)
>>>>>>>
>>>>>>> diff --git
>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.inf 
>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.inf
>>>>>>> index 1e1a639857e0..a7ac4830b3d4 100644
>>>>>>> ---
>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.inf
>>>>>>> +++
>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.inf
>>>>>>> @@ -53,6 +53,7 @@ [LibraryClasses]
>>>>>>>    SecurityManagementLib
>>>>>>>    PeCoffLib
>>>>>>>    TpmMeasurementLib
>>>>>>> +  SafeIntLib
>>>>>>>
>>>>>>>  [Protocols]
>>>>>>>    gEfiFirmwareVolume2ProtocolGuid       ## SOMETIMES_CONSUMES
>>>>>>> diff --git
>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.h 
>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.h
>>>>>>> index 17955ff9774c..060273917d5d 100644
>>>>>>> ---
>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.h
>>>>>>> +++
>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.h
>>>>>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent  
>>>>>>> #include <Library/DevicePathLib.h>  #include 
>>>>>>> <Library/SecurityManagementLib.h>  #include <Library/PeCoffLib.h>
>>>>>>> +#include <Library/SafeIntLib.h>
>>>>>>>  #include <Protocol/FirmwareVolume2.h>  #include 
>>>>>>> <Protocol/DevicePath.h>  #include <Protocol/BlockIo.h> diff --git
>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.c 
>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.c
>>>>>>> index 36b87e16d53d..dbc03e28c05b 100644
>>>>>>> ---
>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>> .c
>>>>>>> +++
>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.c
>>>>>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
>>>>>>>    EFI_STATUS                           HashStatus;
>>>>>>>    EFI_STATUS                           DbStatus;
>>>>>>>    BOOLEAN                              IsFound;
>>>>>>> +  UINT32                               AlignedLength;
>>>>>>> +  UINT32                               Result;
>>>>>>> +  EFI_STATUS                           AddStatus;
>>>>>>> +  BOOLEAN                              IsAuthDataAssigned;
>>>>>>>
>>>>>>>    SignatureList     = NULL;
>>>>>>>    SignatureListSize = 0;
>>>>>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
>>>>>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
>>>>>>>    IsVerified        = FALSE;
>>>>>>>    IsFound           = FALSE;
>>>>>>> +  Result            = 0;
>>>>>>>
>>>>>>>    //
>>>>>>>    // Check the image type and get policy setting.
>>>>>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
>>>>>>>    // The first certificate starts at offset 
>>>>>>> (SecDataDir->VirtualAddress) from
>>> the
>>>>> start of the file.
>>>>>>>    //
>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>>>>> dwLength))) {
>>>>>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet < 
>>>>>>> + (SecDataDir-
>>>>>> VirtualAddress + SecDataDir->Size));) {
>>>>>>> +    IsAuthDataAssigned = FALSE;
>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE
>>> (WinCertificate-
>>>>>> dwLength);
>>>>>>
>>>>>> I disagree with this patch.
>>>>>>
>>>>>> The primary reason for my disagreement is that the bug report 
>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is 
>>>>>> inexact, and so this patch tries to fix the wrong thing.
>>>>>>
>>>>>> With edk2 master at commit 65904cdbb33c, it is *not* possible to 
>>>>>> overflow the OffSet variable to zero with "WinCertificate->dwLength"
>>>>>> *purely*, and cause an endless loop. Note that we have (at commit
>>>>>> 65904cdbb33c):
>>>>>>
>>>>>>   for (OffSet = SecDataDir->VirtualAddress;
>>>>>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE 
>>>>>> (WinCertificate-
>>>>>> dwLength))) {
>>>>>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) 
>>>>>> <= sizeof
>>>>> (WIN_CERTIFICATE) ||
>>>>>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>> WinCertificate-
>>>>>> dwLength) {
>>>>>>       break;
>>>>>>     }
>>>>>>
>>>>>> The last sub-condition checks whether the Security Data Directory 
>>>>>> has enough room left for "WinCertificate->dwLength". If not, then 
>>>>>> we break out of the loop.
>>>>>>
>>>>>> If we *do* have enough room, that is:
>>>>>>
>>>>>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >=
>>> WinCertificate-
>>>>>> dwLength
>>>>>>
>>>>>> then we have (by adding OffSet to both sides):
>>>>>>
>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet + 
>>>>>> WinCertificate- dwLength
>>>>>>
>>>>>> The left hand side is a known-good UINT32, and so incrementing 
>>>>>> OffSet (a
>>>>>> UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32) 
>>>>>> does not cause an overflow.
>>>>>>
>>>>>> Instead, the problem is with the alignment. The "if" statement 
>>>>>> checks whether we have enough room for "dwLength", but then 
>>>>>> "OffSet" is advanced by "dwLength" *aligned up* to the next 
>>>>>> multiple of 8. And that may indeed cause various overflows.
>>>>>>
>>>>>> Now, the main problem with the present patch is that it does not 
>>>>>> fix one of those overflows. Namely, consider that "dwLength" is 
>>>>>> very close to
>>>>>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then aligning 
>>>>>> it up to the next multiple of 8 will yield 0. In other words, "AlignedLength"
>>>>>> will be zero.
>>>>>>
>>>>>> And when that happens, there's going to be an infinite loop just 
>>>>>> the
>>>>>> same: "OffSet" will not be zero, but it will be *stuck*. The
>>>>>> SafeUint32Add() call at the bottom will succeed, but it will not 
>>>>>> change the value of "OffSet".
>>>>>>
>>>>>> More at the bottom.
>>>>>>
>>>>>>
>>>>>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) 
>>>>>>> <= sizeof
>>>>> (WIN_CERTIFICATE) ||
>>>>>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) 
>>>>>>> <
>>>>> WinCertificate->dwLength) {
>>>>>>>        break;
>>>>>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
>>>>>>>        }
>>>>>>>        AuthData   = PkcsCertData->CertData;
>>>>>>>        AuthDataSize = PkcsCertData->Hdr.dwLength - 
>>>>>>> sizeof(PkcsCertData-
>>>> Hdr);
>>>>>>> +      IsAuthDataAssigned = TRUE;
>>>>>>> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>>      } else if (WinCertificate->wCertificateType ==
>>> WIN_CERT_TYPE_EFI_GUID)
>>>>> {
>>>>>>>        //
>>>>>>>        // The certificate is formatted as 
>>>>>>> WIN_CERTIFICATE_UEFI_GUID which
>>> is
>>>>> described in UEFI Spec.
>>>>>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
>>>>>>>        if (WinCertUefiGuid->Hdr.dwLength <=
>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
>>>>>>>          break;
>>>>>>>        }
>>>>>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid))
>>> {
>>>>>>> -        continue;
>>>>>>> +      if (CompareGuid (&WinCertUefiGuid->CertType, 
>>>>>>> + &gEfiCertPkcs7Guid))
>>> {
>>>>>>> +        AuthData = WinCertUefiGuid->CertData;
>>>>>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>> +        IsAuthDataAssigned = TRUE;
>>>>>>> +        HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>>        }
>>>>>>> -      AuthData = WinCertUefiGuid->CertData;
>>>>>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>>      } else {
>>>>>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
>>>>>>>          break;
>>>>>>>        }
>>>>>>> -      continue;
>>>>>>>      }
>>>>>>>
>>>>>>> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>> -    if (EFI_ERROR (HashStatus)) {
>>>>>>> -      continue;
>>>>>>> -    }
>>>>>>> -
>>>>>>> -    //
>>>>>>> -    // Check the digital signature against the revoked certificate in
>>> forbidden
>>>>> database (dbx).
>>>>>>> -    //
>>>>>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>>> -      IsVerified = FALSE;
>>>>>>> -      break;
>>>>>>> -    }
>>>>>>> -
>>>>>>> -    //
>>>>>>> -    // Check the digital signature against the valid certificate in allowed
>>>>> database (db).
>>>>>>> -    //
>>>>>>> -    if (!IsVerified) {
>>>>>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>>> -        IsVerified = TRUE;
>>>>>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
>>>>>>> +      //
>>>>>>> +      // Check the digital signature against the revoked 
>>>>>>> + certificate in
>>> forbidden
>>>>> database (dbx).
>>>>>>> +      //
>>>>>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>>> +        IsVerified = FALSE;
>>>>>>> +        break;
>>>>>>>        }
>>>>>>> -    }
>>>>>>>
>>>>>>> -    //
>>>>>>> -    // Check the image's hash value.
>>>>>>> -    //
>>>>>>> -    DbStatus = IsSignatureFoundInDatabase (
>>>>>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>> -                 mImageDigest,
>>>>>>> -                 &mCertType,
>>>>>>> -                 mImageDigestSize,
>>>>>>> -                 &IsFound
>>>>>>> -                 );
>>>>>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
>>> but %s
>>>>> hash of image is found in DBX.\n", mHashTypeStr));
>>>>>>> -      IsVerified = FALSE;
>>>>>>> -      break;
>>>>>>> -    }
>>>>>>> +      //
>>>>>>> +      // Check the digital signature against the valid 
>>>>>>> + certificate in allowed
>>>>> database (db).
>>>>>>> +      //
>>>>>>> +      if (!IsVerified) {
>>>>>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>>> +          IsVerified = TRUE;
>>>>>>> +        }
>>>>>>> +      }
>>>>>>>
>>>>>>> -    if (!IsVerified) {
>>>>>>> +      //
>>>>>>> +      // Check the image's hash value.
>>>>>>> +      //
>>>>>>>        DbStatus = IsSignatureFoundInDatabase (
>>>>>>> -                   EFI_IMAGE_SECURITY_DATABASE,
>>>>>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>>                     mImageDigest,
>>>>>>>                     &mCertType,
>>>>>>>                     mImageDigestSize,
>>>>>>>                     &IsFound
>>>>>>>                     );
>>>>>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>>> -        IsVerified = TRUE;
>>>>>>> -      } else {
>>>>>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
>>> but
>>>>> signature is not allowed by DB and %s hash of image is not found in
>>> DB/DBX.\n",
>>>>> mHashTypeStr));
>>>>>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is 
>>>>>>> + signed
>>>>> but %s hash of image is found in DBX.\n", mHashTypeStr));
>>>>>>> +        IsVerified = FALSE;
>>>>>>> +        break;
>>>>>>>        }
>>>>>>> +
>>>>>>> +      if (!IsVerified) {
>>>>>>> +        DbStatus = IsSignatureFoundInDatabase (
>>>>>>> +                     EFI_IMAGE_SECURITY_DATABASE,
>>>>>>> +                     mImageDigest,
>>>>>>> +                     &mCertType,
>>>>>>> +                     mImageDigestSize,
>>>>>>> +                     &IsFound
>>>>>>> +                     );
>>>>>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>>> +          IsVerified = TRUE;
>>>>>>> +        } else {
>>>>>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is 
>>>>>>> + signed
>>> but
>>>>> signature is not allowed by DB and %s hash of image is not found in
>>> DB/DBX.\n",
>>>>> mHashTypeStr));
>>>>>>> +        }
>>>>>>> +      }
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
>>>>>>> +    if (EFI_ERROR (AddStatus)) {
>>>>>>> +      break;
>>>>>>>      }
>>>>>>> +    OffSet = Result;
>>>>>>>    }
>>>>>>>
>>>>>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) 
>>>>>>> {
>>>>>>>
>>>>>>
>>>>>> There are other (smaller) reasons why I dislike this patch:
>>>>>>
>>>>>> - The "IsAuthDataAssigned" variable is superfluous; we could use 
>>>>>> the existent "AuthData" variable (with a NULL-check and a 
>>>>>> NULL-assignment) similarly.
>>>>>>
>>>>>> - The patch complicates / reorganizes the control flow needlessly. 
>>>>>> This complication originates from placing the checked "OffSet" 
>>>>>> increment at the bottom of the loop, which then requires the 
>>>>>> removal of all the "continue" statements. But we don't need to 
>>>>>> check-and-increment at the bottom. We can keep the increment 
>>>>>> inside the "for" statement, only extend the *existent* room check 
>>>>>> (which I've quoted) to take the alignment into account as well. If 
>>>>>> there is enough room for the alignment in the security data 
>>>>>> directory, then that guarantees there won't be a UINT32 overflow either.
>>>>>>
>>>>>> All in all, I'm proposing the following three patches instead. The 
>>>>>> first two patches are preparation, the last patch is the fix.
>>>>>>
>>>>>> Patch#1:
>>>>>>
>>>>>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep 17
>>> 00:00:00
>>>>> 2001
>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
>>>>>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib: extract  
>>>>>>> SecDataDirEnd, SecDataDirLeft
>>>>>>>
>>>>>>> The following two quantities:
>>>>>>>
>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
>>>>>>>
>>>>>>> are used multiple times in DxeImageVerificationHandler(). 
>>>>>>> Introduce helper variables for them: "SecDataDirEnd" and "SecDataDirLeft", respectively.
>>>>>>> This saves us multiple calculations and significantly simplifies the code.
>>>>>>>
>>>>>>> Note that all three summands above have type UINT32, therefore 
>>>>>>> the new variables are also of type UINT32.
>>>>>>>
>>>>>>> This patch does not change behavior.
>>>>>>>
>>>>>>> (Note that the code already handles the case when the
>>>>>>>
>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>>
>>>>>>> UINT32 addition overflows -- namely, in that case, the 
>>>>>>> certificate loop is never entered, and the corruption check right 
>>>>>>> after the loop fires.)
>>>>>>>
>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>> ---
>>>>>>>  
>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>> ib.c |
>>> 12
>>>>> ++++++++----
>>>>>>>  1 file changed, 8 insertions(+), 4 deletions(-)
>>>>>>>
>>>>>>> diff --git
>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.c 
>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.c
>>>>>>> index 36b87e16d53d..8761980c88aa 100644
>>>>>>> ---
>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>> .c
>>>>>>> +++
>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.c
>>>>>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
>>>>>>>    UINT8                                *AuthData;
>>>>>>>    UINTN                                AuthDataSize;
>>>>>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
>>>>>>> +  UINT32                               SecDataDirEnd;
>>>>>>> +  UINT32                               SecDataDirLeft;
>>>>>>>    UINT32                               OffSet;
>>>>>>>    CHAR16                               *NameStr;
>>>>>>>    RETURN_STATUS                        PeCoffStatus;
>>>>>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
>>>>>>>    // "Attribute Certificate Table".
>>>>>>>    // The first certificate starts at offset 
>>>>>>> (SecDataDir->VirtualAddress) from
>>> the
>>>>> start of the file.
>>>>>>>    //
>>>>>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>> +       OffSet < SecDataDirEnd;
>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE 
>>>>>>> (WinCertificate-
>>>>>> dwLength))) {
>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>>>>> (WIN_CERTIFICATE) ||
>>>>>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>>> WinCertificate->dwLength) {
>>>>>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>>> +        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>        break;
>>>>>>>      }
>>>>>>>
>>>>>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
>>>>>>>      }
>>>>>>>    }
>>>>>>>
>>>>>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) 
>>>>>>> {
>>>>>>> +  if (OffSet != SecDataDirEnd) {
>>>>>>>      //
>>>>>>>      // The Size in Certificate Table or the attribute 
>>>>>>> certificate table is
>>> corrupted.
>>>>>>>      //
>>>>>>> --
>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>
>>>>>>
>>>>>> Patch#2:
>>>>>>
>>>>>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep 17 00:00:00
>>>>> 2001
>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
>>>>>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib: assign  
>>>>>>> WinCertificate after size check
>>>>>>>
>>>>>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) check 
>>>>>>> only guards the de-referencing of the "WinCertificate" pointer. 
>>>>>>> It does not guard the calculation of hte pointer itself:
>>>>>>>
>>>>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>
>>>>>>> This is wrong; if we don't know for sure that we have enough room 
>>>>>>> for a WIN_CERTIFICATE, then even creating such a pointer, not 
>>>>>>> just de-referencing it, may invoke undefined behavior.
>>>>>>>
>>>>>>> Move the pointer calculation after the size check.
>>>>>>>
>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>> ---
>>>>>>>  
>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>> ib.c |
>>> 8
>>>>> +++++---
>>>>>>>  1 file changed, 5 insertions(+), 3 deletions(-)
>>>>>>>
>>>>>>> diff --git
>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.c 
>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.c
>>>>>>> index 8761980c88aa..461ed7cfb5ac 100644
>>>>>>> ---
>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>> .c
>>>>>>> +++
>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.c
>>>>>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>         OffSet < SecDataDirEnd;
>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE 
>>>>>>> (WinCertificate-
>>>>>> dwLength))) {
>>>>>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>>> -        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
>>>>>>> +      break;
>>>>>>> +    }
>>>>>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>        break;
>>>>>>>      }
>>>>>>>
>>>>>>> --
>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>
>>>>>>
>>>>>> Patch#3:
>>>>>>
>>>>>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17 00:00:00
>>>>> 2001
>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
>>>>>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch
>>> alignment
>>>>>>>  overflow (CVE-2019-14562)
>>>>>>>
>>>>>>> The DxeImageVerificationHandler() function currently checks 
>>>>>>> whether "SecDataDir" has enough room for 
>>>>>>> "WinCertificate->dwLength". However,
>>>>> for
>>>>>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to the 
>>>>>>> next multiple of 8. If "WinCertificate->dwLength" is large 
>>>>>>> enough, the alignment will return 0, and "OffSet" will be stuck at the same value.
>>>>>>>
>>>>>>> Check whether "SecDataDir" has room left for both 
>>>>>>> "WinCertificate->dwLength" and the alignment.
>>>>>>>
>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>> ---
>>>>>>>  
>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>> ib.c |
>>> 4
>>>>> +++-
>>>>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>>>>>
>>>>>>> diff --git
>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.c 
>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.c
>>>>>>> index 461ed7cfb5ac..e38eb981b7a0 100644
>>>>>>> ---
>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>> .c
>>>>>>> +++
>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>> ib.c
>>>>>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
>>>>>>>        break;
>>>>>>>      }
>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
>>>>>>> +        (SecDataDirLeft - WinCertificate->dwLength <
>>>>>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
>>>>>>>        break;
>>>>>>>      }
>>>>>>>
>>>>>>> --
>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>
>>>>>>
>>>>>> If Wenyi and the reviewers are OK with these patches, I can submit 
>>>>>> them as a standalone patch series.
>>>>>>
>>>>>> Note that I do not have any reproducer for the issue; the best 
>>>>>> testing that I could offer would be some light-weight Secure Boot 
>>>>>> regression tests.
>>>>>>
>>>>>> Thanks
>>>>>> Laszlo
>>>>>>
>>>>>>
>>>>>> .
>>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>
>>>
>>> 
>>
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-18 17:29                 ` Bret Barkelew
  2020-08-18 23:00                   ` Yao, Jiewen
@ 2020-08-19  9:33                   ` Laszlo Ersek
  1 sibling, 0 replies; 32+ messages in thread
From: Laszlo Ersek @ 2020-08-19  9:33 UTC (permalink / raw)
  To: Bret Barkelew, devel@edk2.groups.io, Yao, Jiewen
  Cc: xiewenyi2@huawei.com, Wang, Jian J, huangming23@huawei.com,
	songdongkuang@huawei.com, Marvin Häuser, Vitaly Cheptsov

On 08/18/20 19:29, Bret Barkelew wrote:
> Jiewen,
> 
> I don’t completely agree with your second point. If the underlying issue is already out of embargo, and we’ve just failed to fix it, that’s not a new issue. I would want to see fixes to the fixes fast-tracked (or at least heavily prioritized), rather than re-entering a full embargo period.

I had the same thought at first -- in the end I guess "it depends". If
we realize that the patch failed to fix the original issue (or a less
obvious issue of the same issue), then I agree the "cat is out of the
bag"; the patch already exposed the vulnerability, so waiting more would
be counter-productive.

OTOH if the fix ends up helping humans recognize a *different*
vulnerability, or even *introduces* a new vulnerability, then that does
seem to deserve an embargo.

I think it depends on the extent of the information that the first patch
exposes. Unfortunately, that "extent" may be vague.

Laszlo

> From: Yao, Jiewen via groups.io<mailto:jiewen.yao=intel.com@groups.io>
> Sent: Tuesday, August 18, 2020 6:12 AM
> To: devel@edk2.groups.io<mailto:devel@edk2.groups.io>; lersek@redhat.com<mailto:lersek@redhat.com>
> Cc: xiewenyi2@huawei.com<mailto:xiewenyi2@huawei.com>; Wang, Jian J<mailto:jian.j.wang@intel.com>; huangming23@huawei.com<mailto:huangming23@huawei.com>; songdongkuang@huawei.com<mailto:songdongkuang@huawei.com>; Marvin Häuser<mailto:mhaeuser@outlook.de>; Vitaly Cheptsov<mailto:vit9696@protonmail.com>
> Subject: [EXTERNAL] Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> 
> Hi Laszlo
> I agree with on your most points.
> It is all about *return of investment* or *risk control*. Like we cannot pursue 100% security because we will bankrupt ourselves if so.
> 
> Here I just raise my concern.
> 1) If we alway allow developers’ low quality patch without test, the overall quality will become lower and lower. Personally I have no confidence to help catch all those issues. You are the role model on code review. But not all people review the code like you. We need both expertise and time for code review.
> 
> 2) I purposely separate security fix from non security one, because the process is different. The embargo might be 6 months. What if we found the security patch does not fix the problem after embargo? Unlike we send one more patch tomorrow, we need wait for another 6 months...
> 
> thank you!
> Yao, Jiewen
> 
> 
>> 在 2020年8月18日,下午6:17,Laszlo Ersek <lersek@redhat.com> 写道:
>>
>> Hi Jiewen,
>>
>> (+Marvin, +Vitaly)
>>
>> On 08/18/20 01:23, Yao, Jiewen wrote:
>>>> -----Original Message-----
>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Laszlo
>>>> Ersek
>>>> Sent: Tuesday, August 18, 2020 12:53 AM
>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
>>>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>
>> [...]
>>
>>> However, I do think the producer is mandatory for a fix or at least a
>>> security fix.
>>> The owner to fix the issue should guarantee the patch is good.
>>> The owner shall never rely on the code reviewer to figure out if the
>>> patch is good and complete.
>>>
>>> I have some bad experience that bug owner just wrote a patch and tried
>>> to fix a problem, without any test.
>>> And it happened passed code review from someone who does not well
>>> understand the problem, but give rb based upon the time pressure.
>>> Later, the fix was approved to be useless.
>>>
>>> In my memory, at least 3 cases were security fix. They are found, just
>>> because they are sensitive, more people took a look later.
>>>    It was simple. It was one-line change.
>>>   But it has not test, and it was wrong.
>>> "It was ridiculous" -- commented by the people who find the so-called
>>> security fix does not fix the issue.
>>
>> Just because sloppy/rushed reviews exist, and just because reviewers
>> operate under time pressure, we should not automatically reject security
>> fixes that come without a reproducer.
>>
>> Some organizations do develop reproducers, but they never share them
>> publicly (for fear of abuse by others).
>>
>> But more importantly, in an open development project, a developer could
>> have time and expertise to contribute a fix, but not to create a
>> reproducer.
>>
>> - If we make contributing harder, fewer people will upstream their
>>  fixes.
>>
>> - If we make contributing harder, then contributions that do make it to
>>  the tree will be of higher quality.
>>
>> Both statements ring true to me -- so it's a tradeoff.
>>
>> (By "we", I mean the edk2 community.)
>>
>>>> Additionally, the exact statement that the bug report does make,
>>>> namely
>>>>
>>>>  it's possible to overflow Offset back to 0 causing an endless loop
>>>>
>>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can
>>>> be overflowed to zero, but the *addend* that is added to OffSet can
>>>> be overflowed to zero. Therefore the infinite loop will arise because
>>>> OffSet remains stuck at its present value, and not because OffSet
>>>> will be re-set to zero.
>>>>
>>>> For the reasons, we can only speculate as to what the actual problem
>>>> is, unless Jian decides to join the discussion and clarifies what he
>>>> had in mind originally.
>>>
>>> [Jiewen] Would you please clarify what do you mean "we" here?
>>> If "we" means the bug dispatcher, it is totally OK. The dispatcher
>>> just assign the bug.
>>> If "we" means the developer assigned to fix the bug, it is NOT OK. The
>>> developer should take the responsibility to understand the problem.
>>
>> By "we", I mean the edk2 community.
>>
>>>> We can write a patch based on code analysis. It's possible to
>>>> identify integer overflows based on code analysis, and it's possible
>>>> to verify the correctness of fixes by code review. Obviously testing
>>>> is always good, but many times, constructing reproducers for such
>>>> issues that were found by code review, is difficult and time
>>>> consuming. We can say that we don't fix vulnerabilities without
>>>> reproducers, or we can say that we make an effort to fix them even if
>>>> all we have is code analysis (and not a reproducer).
>>>
>>> [Jiewen] I would say: yes and no.
>>> Yes, I agree with you that it might be difficult and time consuming to
>>> construct the reproducer.
>>> However, "obviously" is a subject term. Someone may think something is
>>> obvious, but other people does not.
>>> We should be clear the responsibility of the patch provider is to
>>> provide high quality patch.
>>> Having basic unit test is the best way to prove that the fix is good.
>>>
>>> I have seen bad cases when I ask for the test for patch, then the
>>> answer I got is: "I test the windows boot".
>>> But the test - windows boot - has nothing related to the patch. It
>>> only proves no regression, but cannot prove the issue described is
>>> resolved.
>>
>> Right. It would be ideal if every patch came with a unit test. But that
>> also means some folks will contribute less.
>>
>> Consider normal (not security) patches. We require that all function
>> return values be checked (unless it really doesn't matter if a function
>> call fails). If a function call fails, we need to roll back the actions
>> taken thus far. Release resources and so on. This is why we have the
>> "cascade of error handling labels" pattern.
>>
>> But, of course, we don't test every possible error path in the code. So
>> what's the solution there:
>>
>> - reject such patches that carefully construct the error paths, but do
>>  not provide unit tests with complete error path coverage?
>>
>> - say that we don't care about thorough error paths, so let's just hang,
>>  or leak resources, whenever something fails?
>>
>> Personally I prefer the detailed error paths. They need to be written
>> and reviewed carefully. And they can be accepted even if they are not
>> tested with complete coverage.
>>
>> Some people think otherwise; they say no untested (untestable) code
>> should ever be merged.
>>
>> Back to security patches -- creating reproducers usually requires a
>> setup (tools, expertise, time allocation etc) that is different from a
>> "normal" setup. It may require specialized binary format editors,
>> expertise in "penetration testing", and so on.
>>
>> I mostly know the C language rules that pertain to integer and buffer
>> overflows, so I can usually spot their violations in C code, and propose
>> fixes for them too. But I'm not a security researcher, so I don't write
>> exploits as a norm -- I don't even investigate, generally speaking, the
>> potential practical impact of "undefined behavior". When there's a
>> buffer overflow or integer overflow in the code, that's the *end* of the
>> story for me, while it's the *start* of the work for a security
>> researcher.
>>
>> When you require reproducers for all security patches, you restrict the
>> potential contributor pool to security researchers.
>>
>>> Let's think again in this case, if the patch provider does some basic
>>> unit test, he/she may find out the problem by himself/herself.
>>> That can save other people's time to review.
>>>
>>> I don't prefer to move the responsibility from patch provider to the
>>> code reviewer to check if the fix is good.
>>> Otherwise, the code reviewer may be overwhelmed.
>>>
>>> We may clarify and document the role and responsibility in EDKII
>>> clearly. Once that is ready, we can follow the rule.
>>> Before that is ready, in this particular case, I still prefer we have
>>> producer to prove the patch is good.
>>
>> OK, thanks for explaining.
>>
>> Given that I'm unable to create such a PE file (from scratch or by
>> modifying another one), I won't post the patches stand-alone.
>>
>>>> So the above paragraph concerns "correctness". Regarding
>>>> "completeness", I guarantee you that this patch does not fix *all*
>>>> problems related to PE parsing. (See the other BZ tickets.) It does
>>>> fix *one* issue with PE parsing. We can say that we try to fix such
>>>> issues gradually (give different CVE numbers to different issues, and
>>>> address them one at a time), or we can say that we rewrite PE parsing
>>>> from the ground up. (BTW: I have seriously attempted that in the
>>>> past, and I gave up, because the PE format is FUBAR.)
>>>
>>> [Jiewen] Maybe there is misunderstanding.
>>> I do not mean to let patch provider to fix all issue in PE parsing.
>>> Just like we cannot file one Bugzilla to fix all issue in EDKII - it
>>> is unfair.
>>>
>>> What I mean is that the patch provider should guarantee the
>>> correctness and completeness of the issue in the bug report.
>>>
>>> One faked bad example of correctness is:
>>>    A bug report is file to say: the code has overflow class A.
>>>    The factor is: the code has overflow class A at line X and line Y.
>>>    The patch only modified some code at line X, but the overflow
>>>    class A at line X still exists.
>>>
>>> One faked bad example of completeness is:
>>>    A bug report is file to say: the code has overflow class A.
>>>    The factor is: the code has overflow class A at line X and line Y.
>>>    The patch only fixed the overflow class A at line X but not line
>>>    Y.
>>>
>>> The patch provider should take responsibility to do that work
>>> seriously to find out issue in line X and line Y and fix them.
>>> He/she may choose to just fix line X and line Y. Rewrite is whole
>>> module is NOT required.
>>
>> I agree completely.
>>
>> My point was that we need the bug report to be precise, in the first
>> place. If the bug report doesn't clearly identify lines X and Y, we will
>> likely not get the completeness part right.
>>
>> "Clearly identify" may mean spelling out lines X and Y specifically. Or
>> it may mean defining "class A" sufficiently clearly that someone else
>> reading the affected function can find X and Y themselves.
>>
>>> If I can give some comment, I would think about the provide the fix in
>>> BasePeCoffLib.
>>
>> From a software design perspective, you are 100% right.
>>
>> Unfortunately, I can't do it.
>>
>> That's what I mentioned before -- I had tried rewriting BasePeCoffLib,
>> because in my opinion, BasePeCoffLib is unsalvageable in its current
>> form. And I gave up on the rewrite.
>>
>> Please see the following email. I sent it to some people off-list, on
>> 2020-Feb-14:
>>
>>> There are currently four (4) TianoCore security BZs (1957, 1990, 1993,
>>> 2215), embargoed, that describe various ways in which cunningly
>>> crafted PE images can evade Secure Boot verification.
>>>
>>> [...]
>>>
>>> Primarily, I just couldn't find my peace with the idea that fixing
>>> such PE/COFF parsing mistakes (integer overflows, buffer overflows)
>>> *must* be a one-by-one fixing game. I wanted an approach that would
>>> fix these *classes* of vulnerabilities, in PE/COFF parsing.
>>>
>>> So, beginnning of this February I returned to this topic, and spent
>>> two days on prototyping / researching a container / interval based
>>> approach. Here's one of the commit messages, as a way of explaining:
>>>
>>>    OvmfPkg/DxePeCoffValidatorLib: introduce CONTAINER type and helper funcs
>>>
>>>    For validating the well-formedness of a PE/COFF file, introduce the
>>>    CONTAINER type, and some workhorse functions. (The functions added in this
>>>    patch will not be called directly from the code that will process PE/COFF
>>>    structures.)
>>>
>>>    The CONTAINER type describes a contiguous non-empty interval in a PE/COFF
>>>    file (on-disk representation, or in-memory representation). Containers can
>>>    be nested. The data from scalar-sized containers can be read out, as part
>>>    of their creation. For on-disk representations of PE/COFF files, scalar
>>>    reads are permitted; for in-memory representations, no data access is
>>>    permitted (only CONTAINER tracking / nesting).
>>>
>>>    The goals of CONTAINER are the following:
>>>
>>>    - enforce the proper nesting of PE/COFF structures (structure boundaries
>>>      must not be crossed by runs of data);
>>>
>>>    - prevent integer overflows and buffer overflows;
>>>
>>>    - prevent zero-size structures;
>>>
>>>    - prevent infinite nesting by requiring proper sub-intervals;
>>>
>>>    - prevent internal PE/COFF pointers from aliasing each other (unless they
>>>      point at container and containee structures);
>>>
>>>    - terminate nesting at scalar-sized containers;
>>>
>>>    - assuming an array of pointers is processed in increasing element order,
>>>      enforce that the pointed-to objects are located at increasing offsets
>>>      too;
>>>
>>>    - assign human-readable names to PE/COFF structures and fields, for
>>>      debugging PE/COFF malformations.
>>>
>>> Because, several of the vulnerabilities exploited cross-directed and
>>> aliased internal pointers in PE/COFF files.
>>>
>>> Two days of delirious spec reading and coding later, and 2000+ lines
>>> later, I decided that my idea was unviable. The PE/COFF spec was so
>>> incredibly mis-designed and crufty that enforcing the *internal*
>>> consistency of all the size fields and the internal pointers would
>>> inevitably fall into one of the following categories:
>>>
>>> - the checks wouldn't be strict enough, and some nasty images would
>>>  slip through,
>>>
>>> - the checks would be too strict, and some quirky, but valid, images
>>>  would be unjustifiedly caught.
>>>
>>> So I gave up and I've accepted that it remains a whack-a-mole game.
>>> [...]
>>>
>>> (NB: I don't claim that ELF is not similarly brain-damaged.)
>>
>> So now, I've only considered contributing patches for bug#2215 because
>> the code in question resides in DxeImageVerificationLib, and *not* in
>> BasePeCoffLib. I'm not going to touch BasePeCoffLib -- in my opinion,
>> BasePeCoffLib is unfixable without a complete rewrite.
>>
>> I would *like* if BasePeCoffLib were fixable incrementally, but I just
>> don't see how that's possible.
>>
>> In support of my opinion, please open the following bugzilla ticket:
>>
>>  https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.tianocore.org%2Fshow_bug.cgi%3Fid%3D2643&amp;data=02%7C01%7Cbret.barkelew%40microsoft.com%7Cada52c41a3db41968b7408d843785e58%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637333531565237004&amp;sdata=C59ZmrHEONxeui4lkIo1RFTTLUOEuU8OQXUbWoS9JWc%3D&amp;reserved=0
>>
>> and search the comments (with the browser's in-page search feature, such
>> as Ctrl+F) for the following expression:
>>
>>  new PE loader
>>
>> I understand exactly what Vitaly and Marvin meant in those comments. :(
>>
>> Thanks,
>> Laszlo
>>
>>
>>
>>
> 
> 
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-19  9:26                 ` Laszlo Ersek
@ 2020-08-28  3:17                   ` wenyi,xie
  2020-08-28  3:50                     ` Yao, Jiewen
  0 siblings, 1 reply; 32+ messages in thread
From: wenyi,xie @ 2020-08-28  3:17 UTC (permalink / raw)
  To: Laszlo Ersek, Wang, Jian J, devel@edk2.groups.io, Yao, Jiewen
  Cc: songdongkuang@huawei.com, Mathews, John

Hi,Laszlo and everyone,

These days I tried to reproduce the issue,and made some progress. I think there are two way to cause overflow from a mathematical point of view,
1.As Laszlo analysed before, if WinCertificate->dwLength is large enough, close to MAX_UINT32, then (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength)) will cause overflow.
2.(WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength)) is good, OffSet is good, but OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength)) cause overflow.

Here I choose the second way to reproduce the issue, I choose a PE file and sign it with my own db certificate. Then I use binary edit tool to modify the PE file like below,

1.change SecDataDir->Size from 0x5F8 to 0xFFFF1FFF
2.change WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFE
SecDataDir->VirtualAddress in my PE is 0xe000 and no need to change.

As PE file is modified, function PeCoffLoaderGetImageInfo will return error, so I have to remove it so that for loop can be tested in DxeImageVerificationHandler.
The log is as below,

SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFF.
(First Loop)
OffSet=0xE000.
WinCertificate->dwLength=0xFFFF1FFE, ALIGN_SIZE (WinCertificate->dwLength)=0x2.
(Second Loop)
OffSet=0x0.
WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate->dwLength)=0x3.
(Third Loop)
OffSet=0x5A50.
WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate->dwLength)=0x4.
(Forth Loop)
OffSet=0xEA78.
WinCertificate->dwLength=0xAFAFAFAF, ALIGN_SIZE (WinCertificate->dwLength)=0x1.
(Fifth Loop)
OffSet=0xAFB09A28.

As I modify SecDataDir->Size and WinCertificate->dwLength, so in first loop the condition check whether the Security Data Directory has enough room left for "WinCertificate->dwLength" is ok.

if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof (WIN_CERTIFICATE) ||
    (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate->dwLength) {
  break;
}

In the beginning of second loop, WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength) is 0xFFFF2000, offset is 0xE000

OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))

Offset now is 0 and overflow happens. So even if my PE only have one signature, the for loop is still going untill exception happens.


I can't reproduce the issue using the first way, because if WinCertificate->dwLength is close to MAX_UINT32, it means SecDataDir->Size should also close to MAX_UINT32, or the condition check
"(SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate->dwLength" will break. But if SecDataDir->Size is very large, SecDataDir->VirtualAddress have to be smaller than 8 bytes,
because SecDataDir->VirtualAddress + SecDataDir->Size < MAX_UINT32.
SecDataDir->VirtualAddress is the beginning address of the signature, and before SecDataDir->VirtualAddress is the content of origin PE file, I think it's impossible that the size of PE file is only 8 bytes.

As I changed the one line code in DxeImageVerificationHandler to reproduce the issue, I'm not sure whether it's ok.

Thanks
Wenyi

On 2020/8/19 17:26, Laszlo Ersek wrote:
> On 08/18/20 17:18, Mathews, John wrote:
>> I dug up the original report details.  This was noted as a concern during a source code inspection.  There was no demonstration of how it might be triggered.
>>
>> " There is an integer overflow vulnerability in the DxeImageVerificationHandler function when
>> parsing the PE files attribute certificate table. In cases where WinCertificate->dwLength is
>> sufficiently large, it's possible to overflow Offset back to 0 causing an endless loop."
>>
>> The recommendation was to add stricter checking of "Offset" and the embedded length fields of certificate data
>> before using them.
> 
> Thanks for checking!
> 
> Laszlo
> 
>>
>>
>>
>> -----Original Message-----
>> From: Laszlo Ersek <lersek@redhat.com> 
>> Sent: Tuesday, August 18, 2020 1:59 AM
>> To: Wang, Jian J <jian.j.wang@intel.com>; devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>; xiewenyi2@huawei.com
>> Cc: huangming23@huawei.com; songdongkuang@huawei.com; Mathews, John <john.mathews@intel.com>
>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>
>> On 08/18/20 04:10, Wang, Jian J wrote:
>>> Laszlo,
>>>
>>> My apologies for the slow response. I'm not the original reporter but 
>>> just the BZ submitter. And I didn't do deep analysis to this issue. 
>>> The issues was reported from one internal team. Add John in loop to see if he knows more about it or not.
>>>
>>> My superficial understanding on such issue is that, if there's 
>>> "potential" issue in theory and hard to reproduce, it's still worthy 
>>> of using an alternative way to replace the original implementation 
>>> with no "potential" issue at all. Maybe we don't have to prove old way is something wrong but must prove that the new way is really safe.
>>
>> I agree, thanks.
>>
>> It would be nice to hear more from the internal team about the originally reported (even if hard-to-trigger) issue.
>>
>> Thanks!
>> Laszlo
>>
>>>
>>> Regards,
>>> Jian
>>>
>>>> -----Original Message-----
>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Laszlo 
>>>> Ersek
>>>> Sent: Tuesday, August 18, 2020 12:53 AM
>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; 
>>>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1] 
>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>
>>>> Hi Jiewen,
>>>>
>>>> On 08/14/20 10:53, Yao, Jiewen wrote:
>>>>>> To Jiewen,
>>>>>> Sorry, I don't have environment to reproduce the issue.
>>>>>
>>>>> Please help me understand, if you don’t have environment to 
>>>>> reproduce the
>>>> issue, how do you guarantee that your patch does fix the problem and 
>>>> we don’t have any other vulnerabilities?
>>>>
>>>> The original bug report in
>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is seriously 
>>>> lacking. It does not go into detail about the alleged integer overflow.
>>>> It does not quote the code, does not explain the control flow, does 
>>>> not identify the exact edk2 commit at which the vulnerability exists.
>>>>
>>>> The bug report also does not offer a reproducer.
>>>>
>>>> Additionally, the exact statement that the bug report does make, 
>>>> namely
>>>>
>>>>   it's possible to overflow Offset back to 0 causing an endless loop
>>>>
>>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can 
>>>> be overflowed to zero, but the *addend* that is added to OffSet can 
>>>> be overflowed to zero. Therefore the infinite loop will arise because 
>>>> OffSet remains stuck at its present value, and not because OffSet 
>>>> will be re-set to zero.
>>>>
>>>> For the reasons, we can only speculate as to what the actual problem 
>>>> is, unless Jian decides to join the discussion and clarifies what he 
>>>> had in mind originally.
>>>>
>>>> My understanding (or even "reconstruction") of the vulnerability is 
>>>> described above, and in the patches that I proposed.
>>>>
>>>> We can write a patch based on code analysis. It's possible to 
>>>> identify integer overflows based on code analysis, and it's possible 
>>>> to verify the correctness of fixes by code review. Obviously testing 
>>>> is always good, but many times, constructing reproducers for such 
>>>> issues that were found by code review, is difficult and time 
>>>> consuming. We can say that we don't fix vulnerabilities without 
>>>> reproducers, or we can say that we make an effort to fix them even if 
>>>> all we have is code analysis (and not a reproducer).
>>>>
>>>> So the above paragraph concerns "correctness". Regarding 
>>>> "completeness", I guarantee you that this patch does not fix *all* 
>>>> problems related to PE parsing. (See the other BZ tickets.) It does 
>>>> fix *one* issue with PE parsing. We can say that we try to fix such 
>>>> issues gradually (give different CVE numbers to different issues, and 
>>>> address them one at a time), or we can say that we rewrite PE parsing from the ground up.
>>>> (BTW: I have seriously attempted that in the past, and I gave up, 
>>>> because the PE format is FUBAR.)
>>>>
>>>> In summary:
>>>>
>>>> - the problem statement is unclear,
>>>>
>>>> - it seems like there is indeed an integer overflow problem in the 
>>>> SecDataDir parsing loop, but it's uncertain whether the bug reporter 
>>>> had exactly that in mind
>>>>
>>>> - PE parsing is guaranteed to have other vulnerabilities elsewhere in 
>>>> edk2, but I'm currently unaware of other such issues in 
>>>> DxeImageVerificationLib specifically
>>>>
>>>> - even if there are other such problems (in DxeImageVerificationLib 
>>>> or elswehere), fixing this bug that we know about is likely 
>>>> worthwhile
>>>>
>>>> - for many such bugs, constructing a reproducer is difficult and time 
>>>> consuming; code analysis, and *regression-testing* are frequently the 
>>>> only tools we have. That doesn't mean we should ignore this class of bugs.
>>>>
>>>> (Fixing integer overflows retro-actively is more difficult than 
>>>> writing overflow-free code in the first place, but that ship has 
>>>> sailed; so we can only fight these bugs incrementally now, unless we 
>>>> can rewrite PE parsing with a new data structure from the ground up. 
>>>> Again I tried that and gave up, because the spec is not public, and 
>>>> what I did manage to learn about PE, showed that it was insanely 
>>>> over-engineered. I'm not saying that other binary / executable 
>>>> formats are better, of course.)
>>>>
>>>> Please check out my patches (inlined elsewhere in this thread), and 
>>>> comment whether you'd like me to post them to the list as a 
>>>> standalone series.
>>>>
>>>> Jian: it wouldn't hurt if you commented as well.
>>>>
>>>> Thanks
>>>> Laszlo
>>>>
>>>>>> -----Original Message-----
>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>>>> wenyi,xie
>>>>>> via groups.io
>>>>>> Sent: Friday, August 14, 2020 3:54 PM
>>>>>> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io; Yao, 
>>>>>> Jiewen <jiewen.yao@intel.com>; Wang, Jian J <jian.j.wang@intel.com>
>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1] 
>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>
>>>>>> To Laszlo,
>>>>>> Thank you for your detailed description, I agree with what you 
>>>>>> analyzed and
>>>> I'm
>>>>>> OK with your patches, it's
>>>>>> correct and much simpler.
>>>>>>
>>>>>> To Jiewen,
>>>>>> Sorry, I don't have environment to reproduce the issue.
>>>>>>
>>>>>> Thanks
>>>>>> Wenyi
>>>>>>
>>>>>> On 2020/8/14 2:50, Laszlo Ersek wrote:
>>>>>>> On 08/13/20 13:55, Wenyi Xie wrote:
>>>>>>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
>>>>>>>>
>>>>>>>> There is an integer overflow vulnerability in 
>>>>>>>> DxeImageVerificationHandler function when parsing the PE files 
>>>>>>>> attribute certificate table. In cases where 
>>>>>>>> WinCertificate->dwLength is sufficiently large, it's possible to overflow Offset back to 0 causing an endless loop.
>>>>>>>>
>>>>>>>> Check offset inbetween VirtualAddress and VirtualAddress + Size.
>>>>>>>> Using SafeintLib to do offset addition with result check.
>>>>>>>>
>>>>>>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>>>>>>>> Cc: Jian J Wang <jian.j.wang@intel.com>
>>>>>>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>>>>>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
>>>>>>>> ---
>>>>>>>>
>>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.inf
>>>> |
>>>>>> 1 +
>>>>>>>>
>>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.h
>>>> |
>>>>>> 1 +
>>>>>>>>
>>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.c
>>>> |
>>>>>> 111 +++++++++++---------
>>>>>>>>  3 files changed, 63 insertions(+), 50 deletions(-)
>>>>>>>>
>>>>>>>> diff --git
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.inf 
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.inf
>>>>>>>> index 1e1a639857e0..a7ac4830b3d4 100644
>>>>>>>> ---
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.inf
>>>>>>>> +++
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.inf
>>>>>>>> @@ -53,6 +53,7 @@ [LibraryClasses]
>>>>>>>>    SecurityManagementLib
>>>>>>>>    PeCoffLib
>>>>>>>>    TpmMeasurementLib
>>>>>>>> +  SafeIntLib
>>>>>>>>
>>>>>>>>  [Protocols]
>>>>>>>>    gEfiFirmwareVolume2ProtocolGuid       ## SOMETIMES_CONSUMES
>>>>>>>> diff --git
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.h 
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.h
>>>>>>>> index 17955ff9774c..060273917d5d 100644
>>>>>>>> ---
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.h
>>>>>>>> +++
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.h
>>>>>>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
>>>>>>>> #include <Library/DevicePathLib.h>  #include 
>>>>>>>> <Library/SecurityManagementLib.h>  #include <Library/PeCoffLib.h>
>>>>>>>> +#include <Library/SafeIntLib.h>
>>>>>>>>  #include <Protocol/FirmwareVolume2.h>  #include 
>>>>>>>> <Protocol/DevicePath.h>  #include <Protocol/BlockIo.h> diff --git
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.c 
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.c
>>>>>>>> index 36b87e16d53d..dbc03e28c05b 100644
>>>>>>>> ---
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>> .c
>>>>>>>> +++
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.c
>>>>>>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
>>>>>>>>    EFI_STATUS                           HashStatus;
>>>>>>>>    EFI_STATUS                           DbStatus;
>>>>>>>>    BOOLEAN                              IsFound;
>>>>>>>> +  UINT32                               AlignedLength;
>>>>>>>> +  UINT32                               Result;
>>>>>>>> +  EFI_STATUS                           AddStatus;
>>>>>>>> +  BOOLEAN                              IsAuthDataAssigned;
>>>>>>>>
>>>>>>>>    SignatureList     = NULL;
>>>>>>>>    SignatureListSize = 0;
>>>>>>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
>>>>>>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
>>>>>>>>    IsVerified        = FALSE;
>>>>>>>>    IsFound           = FALSE;
>>>>>>>> +  Result            = 0;
>>>>>>>>
>>>>>>>>    //
>>>>>>>>    // Check the image type and get policy setting.
>>>>>>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
>>>>>>>>    // The first certificate starts at offset 
>>>>>>>> (SecDataDir->VirtualAddress) from
>>>> the
>>>>>> start of the file.
>>>>>>>>    //
>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>>>>>> dwLength))) {
>>>>>>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet < 
>>>>>>>> + (SecDataDir-
>>>>>>> VirtualAddress + SecDataDir->Size));) {
>>>>>>>> +    IsAuthDataAssigned = FALSE;
>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE
>>>> (WinCertificate-
>>>>>>> dwLength);
>>>>>>>
>>>>>>> I disagree with this patch.
>>>>>>>
>>>>>>> The primary reason for my disagreement is that the bug report 
>>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is 
>>>>>>> inexact, and so this patch tries to fix the wrong thing.
>>>>>>>
>>>>>>> With edk2 master at commit 65904cdbb33c, it is *not* possible to 
>>>>>>> overflow the OffSet variable to zero with "WinCertificate->dwLength"
>>>>>>> *purely*, and cause an endless loop. Note that we have (at commit
>>>>>>> 65904cdbb33c):
>>>>>>>
>>>>>>>   for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE 
>>>>>>> (WinCertificate-
>>>>>>> dwLength))) {
>>>>>>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) 
>>>>>>> <= sizeof
>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>> WinCertificate-
>>>>>>> dwLength) {
>>>>>>>       break;
>>>>>>>     }
>>>>>>>
>>>>>>> The last sub-condition checks whether the Security Data Directory 
>>>>>>> has enough room left for "WinCertificate->dwLength". If not, then 
>>>>>>> we break out of the loop.
>>>>>>>
>>>>>>> If we *do* have enough room, that is:
>>>>>>>
>>>>>>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >=
>>>> WinCertificate-
>>>>>>> dwLength
>>>>>>>
>>>>>>> then we have (by adding OffSet to both sides):
>>>>>>>
>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet + 
>>>>>>> WinCertificate- dwLength
>>>>>>>
>>>>>>> The left hand side is a known-good UINT32, and so incrementing 
>>>>>>> OffSet (a
>>>>>>> UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32) 
>>>>>>> does not cause an overflow.
>>>>>>>
>>>>>>> Instead, the problem is with the alignment. The "if" statement 
>>>>>>> checks whether we have enough room for "dwLength", but then 
>>>>>>> "OffSet" is advanced by "dwLength" *aligned up* to the next 
>>>>>>> multiple of 8. And that may indeed cause various overflows.
>>>>>>>
>>>>>>> Now, the main problem with the present patch is that it does not 
>>>>>>> fix one of those overflows. Namely, consider that "dwLength" is 
>>>>>>> very close to
>>>>>>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then aligning 
>>>>>>> it up to the next multiple of 8 will yield 0. In other words, "AlignedLength"
>>>>>>> will be zero.
>>>>>>>
>>>>>>> And when that happens, there's going to be an infinite loop just 
>>>>>>> the
>>>>>>> same: "OffSet" will not be zero, but it will be *stuck*. The
>>>>>>> SafeUint32Add() call at the bottom will succeed, but it will not 
>>>>>>> change the value of "OffSet".
>>>>>>>
>>>>>>> More at the bottom.
>>>>>>>
>>>>>>>
>>>>>>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) 
>>>>>>>> <= sizeof
>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) 
>>>>>>>> <
>>>>>> WinCertificate->dwLength) {
>>>>>>>>        break;
>>>>>>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
>>>>>>>>        }
>>>>>>>>        AuthData   = PkcsCertData->CertData;
>>>>>>>>        AuthDataSize = PkcsCertData->Hdr.dwLength - 
>>>>>>>> sizeof(PkcsCertData-
>>>>> Hdr);
>>>>>>>> +      IsAuthDataAssigned = TRUE;
>>>>>>>> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>>>      } else if (WinCertificate->wCertificateType ==
>>>> WIN_CERT_TYPE_EFI_GUID)
>>>>>> {
>>>>>>>>        //
>>>>>>>>        // The certificate is formatted as 
>>>>>>>> WIN_CERTIFICATE_UEFI_GUID which
>>>> is
>>>>>> described in UEFI Spec.
>>>>>>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
>>>>>>>>        if (WinCertUefiGuid->Hdr.dwLength <=
>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
>>>>>>>>          break;
>>>>>>>>        }
>>>>>>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid))
>>>> {
>>>>>>>> -        continue;
>>>>>>>> +      if (CompareGuid (&WinCertUefiGuid->CertType, 
>>>>>>>> + &gEfiCertPkcs7Guid))
>>>> {
>>>>>>>> +        AuthData = WinCertUefiGuid->CertData;
>>>>>>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>>> +        IsAuthDataAssigned = TRUE;
>>>>>>>> +        HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>>>        }
>>>>>>>> -      AuthData = WinCertUefiGuid->CertData;
>>>>>>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>>>      } else {
>>>>>>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
>>>>>>>>          break;
>>>>>>>>        }
>>>>>>>> -      continue;
>>>>>>>>      }
>>>>>>>>
>>>>>>>> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>>> -    if (EFI_ERROR (HashStatus)) {
>>>>>>>> -      continue;
>>>>>>>> -    }
>>>>>>>> -
>>>>>>>> -    //
>>>>>>>> -    // Check the digital signature against the revoked certificate in
>>>> forbidden
>>>>>> database (dbx).
>>>>>>>> -    //
>>>>>>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>>>> -      IsVerified = FALSE;
>>>>>>>> -      break;
>>>>>>>> -    }
>>>>>>>> -
>>>>>>>> -    //
>>>>>>>> -    // Check the digital signature against the valid certificate in allowed
>>>>>> database (db).
>>>>>>>> -    //
>>>>>>>> -    if (!IsVerified) {
>>>>>>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>>>> -        IsVerified = TRUE;
>>>>>>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
>>>>>>>> +      //
>>>>>>>> +      // Check the digital signature against the revoked 
>>>>>>>> + certificate in
>>>> forbidden
>>>>>> database (dbx).
>>>>>>>> +      //
>>>>>>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>>>> +        IsVerified = FALSE;
>>>>>>>> +        break;
>>>>>>>>        }
>>>>>>>> -    }
>>>>>>>>
>>>>>>>> -    //
>>>>>>>> -    // Check the image's hash value.
>>>>>>>> -    //
>>>>>>>> -    DbStatus = IsSignatureFoundInDatabase (
>>>>>>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>>> -                 mImageDigest,
>>>>>>>> -                 &mCertType,
>>>>>>>> -                 mImageDigestSize,
>>>>>>>> -                 &IsFound
>>>>>>>> -                 );
>>>>>>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
>>>> but %s
>>>>>> hash of image is found in DBX.\n", mHashTypeStr));
>>>>>>>> -      IsVerified = FALSE;
>>>>>>>> -      break;
>>>>>>>> -    }
>>>>>>>> +      //
>>>>>>>> +      // Check the digital signature against the valid 
>>>>>>>> + certificate in allowed
>>>>>> database (db).
>>>>>>>> +      //
>>>>>>>> +      if (!IsVerified) {
>>>>>>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>>>> +          IsVerified = TRUE;
>>>>>>>> +        }
>>>>>>>> +      }
>>>>>>>>
>>>>>>>> -    if (!IsVerified) {
>>>>>>>> +      //
>>>>>>>> +      // Check the image's hash value.
>>>>>>>> +      //
>>>>>>>>        DbStatus = IsSignatureFoundInDatabase (
>>>>>>>> -                   EFI_IMAGE_SECURITY_DATABASE,
>>>>>>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>>>                     mImageDigest,
>>>>>>>>                     &mCertType,
>>>>>>>>                     mImageDigestSize,
>>>>>>>>                     &IsFound
>>>>>>>>                     );
>>>>>>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>>>> -        IsVerified = TRUE;
>>>>>>>> -      } else {
>>>>>>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed
>>>> but
>>>>>> signature is not allowed by DB and %s hash of image is not found in
>>>> DB/DBX.\n",
>>>>>> mHashTypeStr));
>>>>>>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is 
>>>>>>>> + signed
>>>>>> but %s hash of image is found in DBX.\n", mHashTypeStr));
>>>>>>>> +        IsVerified = FALSE;
>>>>>>>> +        break;
>>>>>>>>        }
>>>>>>>> +
>>>>>>>> +      if (!IsVerified) {
>>>>>>>> +        DbStatus = IsSignatureFoundInDatabase (
>>>>>>>> +                     EFI_IMAGE_SECURITY_DATABASE,
>>>>>>>> +                     mImageDigest,
>>>>>>>> +                     &mCertType,
>>>>>>>> +                     mImageDigestSize,
>>>>>>>> +                     &IsFound
>>>>>>>> +                     );
>>>>>>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>>>> +          IsVerified = TRUE;
>>>>>>>> +        } else {
>>>>>>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is 
>>>>>>>> + signed
>>>> but
>>>>>> signature is not allowed by DB and %s hash of image is not found in
>>>> DB/DBX.\n",
>>>>>> mHashTypeStr));
>>>>>>>> +        }
>>>>>>>> +      }
>>>>>>>> +    }
>>>>>>>> +
>>>>>>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
>>>>>>>> +    if (EFI_ERROR (AddStatus)) {
>>>>>>>> +      break;
>>>>>>>>      }
>>>>>>>> +    OffSet = Result;
>>>>>>>>    }
>>>>>>>>
>>>>>>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) 
>>>>>>>> {
>>>>>>>>
>>>>>>>
>>>>>>> There are other (smaller) reasons why I dislike this patch:
>>>>>>>
>>>>>>> - The "IsAuthDataAssigned" variable is superfluous; we could use 
>>>>>>> the existent "AuthData" variable (with a NULL-check and a 
>>>>>>> NULL-assignment) similarly.
>>>>>>>
>>>>>>> - The patch complicates / reorganizes the control flow needlessly. 
>>>>>>> This complication originates from placing the checked "OffSet" 
>>>>>>> increment at the bottom of the loop, which then requires the 
>>>>>>> removal of all the "continue" statements. But we don't need to 
>>>>>>> check-and-increment at the bottom. We can keep the increment 
>>>>>>> inside the "for" statement, only extend the *existent* room check 
>>>>>>> (which I've quoted) to take the alignment into account as well. If 
>>>>>>> there is enough room for the alignment in the security data 
>>>>>>> directory, then that guarantees there won't be a UINT32 overflow either.
>>>>>>>
>>>>>>> All in all, I'm proposing the following three patches instead. The 
>>>>>>> first two patches are preparation, the last patch is the fix.
>>>>>>>
>>>>>>> Patch#1:
>>>>>>>
>>>>>>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep 17
>>>> 00:00:00
>>>>>> 2001
>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
>>>>>>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib: extract
>>>>>>>> SecDataDirEnd, SecDataDirLeft
>>>>>>>>
>>>>>>>> The following two quantities:
>>>>>>>>
>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
>>>>>>>>
>>>>>>>> are used multiple times in DxeImageVerificationHandler(). 
>>>>>>>> Introduce helper variables for them: "SecDataDirEnd" and "SecDataDirLeft", respectively.
>>>>>>>> This saves us multiple calculations and significantly simplifies the code.
>>>>>>>>
>>>>>>>> Note that all three summands above have type UINT32, therefore 
>>>>>>>> the new variables are also of type UINT32.
>>>>>>>>
>>>>>>>> This patch does not change behavior.
>>>>>>>>
>>>>>>>> (Note that the code already handles the case when the
>>>>>>>>
>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>>>
>>>>>>>> UINT32 addition overflows -- namely, in that case, the 
>>>>>>>> certificate loop is never entered, and the corruption check right 
>>>>>>>> after the loop fires.)
>>>>>>>>
>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>> ---
>>>>>>>>
>>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.c |
>>>> 12
>>>>>> ++++++++----
>>>>>>>>  1 file changed, 8 insertions(+), 4 deletions(-)
>>>>>>>>
>>>>>>>> diff --git
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.c 
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.c
>>>>>>>> index 36b87e16d53d..8761980c88aa 100644
>>>>>>>> ---
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>> .c
>>>>>>>> +++
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.c
>>>>>>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
>>>>>>>>    UINT8                                *AuthData;
>>>>>>>>    UINTN                                AuthDataSize;
>>>>>>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
>>>>>>>> +  UINT32                               SecDataDirEnd;
>>>>>>>> +  UINT32                               SecDataDirLeft;
>>>>>>>>    UINT32                               OffSet;
>>>>>>>>    CHAR16                               *NameStr;
>>>>>>>>    RETURN_STATUS                        PeCoffStatus;
>>>>>>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
>>>>>>>>    // "Attribute Certificate Table".
>>>>>>>>    // The first certificate starts at offset 
>>>>>>>> (SecDataDir->VirtualAddress) from
>>>> the
>>>>>> start of the file.
>>>>>>>>    //
>>>>>>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>> +       OffSet < SecDataDirEnd;
>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE 
>>>>>>>> (WinCertificate-
>>>>>>> dwLength))) {
>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>>>> WinCertificate->dwLength) {
>>>>>>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>>>> +        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>        break;
>>>>>>>>      }
>>>>>>>>
>>>>>>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
>>>>>>>>      }
>>>>>>>>    }
>>>>>>>>
>>>>>>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) 
>>>>>>>> {
>>>>>>>> +  if (OffSet != SecDataDirEnd) {
>>>>>>>>      //
>>>>>>>>      // The Size in Certificate Table or the attribute 
>>>>>>>> certificate table is
>>>> corrupted.
>>>>>>>>      //
>>>>>>>> --
>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>
>>>>>>>
>>>>>>> Patch#2:
>>>>>>>
>>>>>>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep 17 00:00:00
>>>>>> 2001
>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
>>>>>>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib: assign 
>>>>>>>> WinCertificate after size check
>>>>>>>>
>>>>>>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) check 
>>>>>>>> only guards the de-referencing of the "WinCertificate" pointer. 
>>>>>>>> It does not guard the calculation of hte pointer itself:
>>>>>>>>
>>>>>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>
>>>>>>>> This is wrong; if we don't know for sure that we have enough room 
>>>>>>>> for a WIN_CERTIFICATE, then even creating such a pointer, not 
>>>>>>>> just de-referencing it, may invoke undefined behavior.
>>>>>>>>
>>>>>>>> Move the pointer calculation after the size check.
>>>>>>>>
>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>> ---
>>>>>>>>
>>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.c |
>>>> 8
>>>>>> +++++---
>>>>>>>>  1 file changed, 5 insertions(+), 3 deletions(-)
>>>>>>>>
>>>>>>>> diff --git
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.c 
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.c
>>>>>>>> index 8761980c88aa..461ed7cfb5ac 100644
>>>>>>>> ---
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>> .c
>>>>>>>> +++
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.c
>>>>>>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>         OffSet < SecDataDirEnd;
>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE 
>>>>>>>> (WinCertificate-
>>>>>>> dwLength))) {
>>>>>>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>>>> -        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
>>>>>>>> +      break;
>>>>>>>> +    }
>>>>>>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>        break;
>>>>>>>>      }
>>>>>>>>
>>>>>>>> --
>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>
>>>>>>>
>>>>>>> Patch#3:
>>>>>>>
>>>>>>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17 00:00:00
>>>>>> 2001
>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
>>>>>>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch
>>>> alignment
>>>>>>>>  overflow (CVE-2019-14562)
>>>>>>>>
>>>>>>>> The DxeImageVerificationHandler() function currently checks 
>>>>>>>> whether "SecDataDir" has enough room for 
>>>>>>>> "WinCertificate->dwLength". However,
>>>>>> for
>>>>>>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to the 
>>>>>>>> next multiple of 8. If "WinCertificate->dwLength" is large 
>>>>>>>> enough, the alignment will return 0, and "OffSet" will be stuck at the same value.
>>>>>>>>
>>>>>>>> Check whether "SecDataDir" has room left for both 
>>>>>>>> "WinCertificate->dwLength" and the alignment.
>>>>>>>>
>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>> ---
>>>>>>>>
>>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.c |
>>>> 4
>>>>>> +++-
>>>>>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>>>>>>
>>>>>>>> diff --git
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.c 
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.c
>>>>>>>> index 461ed7cfb5ac..e38eb981b7a0 100644
>>>>>>>> ---
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>> .c
>>>>>>>> +++
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>> ib.c
>>>>>>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
>>>>>>>>        break;
>>>>>>>>      }
>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
>>>>>>>> +        (SecDataDirLeft - WinCertificate->dwLength <
>>>>>>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
>>>>>>>>        break;
>>>>>>>>      }
>>>>>>>>
>>>>>>>> --
>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>
>>>>>>>
>>>>>>> If Wenyi and the reviewers are OK with these patches, I can submit 
>>>>>>> them as a standalone patch series.
>>>>>>>
>>>>>>> Note that I do not have any reproducer for the issue; the best 
>>>>>>> testing that I could offer would be some light-weight Secure Boot 
>>>>>>> regression tests.
>>>>>>>
>>>>>>> Thanks
>>>>>>> Laszlo
>>>>>>>
>>>>>>>
>>>>>>> .
>>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>
>>>>
>>>> 
>>>
>>
> 
> 
> .
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-28  3:17                   ` wenyi,xie
@ 2020-08-28  3:50                     ` Yao, Jiewen
  2020-08-28  6:18                       ` wenyi,xie
  0 siblings, 1 reply; 32+ messages in thread
From: Yao, Jiewen @ 2020-08-28  3:50 UTC (permalink / raw)
  To: devel@edk2.groups.io, xiewenyi2@huawei.com, Laszlo Ersek,
	Wang, Jian J
  Cc: songdongkuang@huawei.com, Mathews, John

HI Wenyi
Thank you very much to take time to reproduce.

I am particular interested in below:
	"As PE file is modified, function PeCoffLoaderGetImageInfo will return error, so I have to remove it so that for loop can be tested in DxeImageVerificationHandler."

By design, the PeCoffLib should catch illegal PE/COFF image and return error. (even it cannot catch all, it should catch most ones).
Other PE/COFF parser may rely on the checker in PeCoffLib and *no need* to duplicate all checkers.
As such, DxeImageVerificationLib (and other PeCoff consumer) just need checks what has passed the check in PeCoffLib.

I don’t think you should remove the checker. If people can remove the checker, I am sure the rest code will be vulnerable, according to the current design.
Could you please to create a case that bypass all checks in PeCoffLib, then run into DxeImageVerificationLib and cause the problem? That would be more valuable for us.

Thank you
Yao Jiewen

> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of wenyi,xie
> via groups.io
> Sent: Friday, August 28, 2020 11:18 AM
> To: Laszlo Ersek <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>;
> devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>
> Cc: songdongkuang@huawei.com; Mathews, John <john.mathews@intel.com>
> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> 
> Hi,Laszlo and everyone,
> 
> These days I tried to reproduce the issue,and made some progress. I think
> there are two way to cause overflow from a mathematical point of view,
> 1.As Laszlo analysed before, if WinCertificate->dwLength is large enough, close
> to MAX_UINT32, then (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >dwLength)) will cause overflow.
> 2.(WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength)) is good,
> OffSet is good, but OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> (WinCertificate->dwLength)) cause overflow.
> 
> Here I choose the second way to reproduce the issue, I choose a PE file and sign
> it with my own db certificate. Then I use binary edit tool to modify the PE file like
> below,
> 
> 1.change SecDataDir->Size from 0x5F8 to 0xFFFF1FFF
> 2.change WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFE
> SecDataDir->VirtualAddress in my PE is 0xe000 and no need to change.
> 
> As PE file is modified, function PeCoffLoaderGetImageInfo will return error, so I
> have to remove it so that for loop can be tested in DxeImageVerificationHandler.
> The log is as below,
> 
> SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFF.
> (First Loop)
> OffSet=0xE000.
> WinCertificate->dwLength=0xFFFF1FFE, ALIGN_SIZE (WinCertificate-
> >dwLength)=0x2.
> (Second Loop)
> OffSet=0x0.
> WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate-
> >dwLength)=0x3.
> (Third Loop)
> OffSet=0x5A50.
> WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate-
> >dwLength)=0x4.
> (Forth Loop)
> OffSet=0xEA78.
> WinCertificate->dwLength=0xAFAFAFAF, ALIGN_SIZE (WinCertificate-
> >dwLength)=0x1.
> (Fifth Loop)
> OffSet=0xAFB09A28.
> 
> As I modify SecDataDir->Size and WinCertificate->dwLength, so in first loop the
> condition check whether the Security Data Directory has enough room left for
> "WinCertificate->dwLength" is ok.
> 
> if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
> (WIN_CERTIFICATE) ||
>     (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
> >dwLength) {
>   break;
> }
> 
> In the beginning of second loop, WinCertificate->dwLength + ALIGN_SIZE
> (WinCertificate->dwLength) is 0xFFFF2000, offset is 0xE000
> 
> OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))
> 
> Offset now is 0 and overflow happens. So even if my PE only have one signature,
> the for loop is still going untill exception happens.
> 
> 
> I can't reproduce the issue using the first way, because if WinCertificate-
> >dwLength is close to MAX_UINT32, it means SecDataDir->Size should also close
> to MAX_UINT32, or the condition check
> "(SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
> >dwLength" will break. But if SecDataDir->Size is very large, SecDataDir-
> >VirtualAddress have to be smaller than 8 bytes,
> because SecDataDir->VirtualAddress + SecDataDir->Size < MAX_UINT32.
> SecDataDir->VirtualAddress is the beginning address of the signature, and before
> SecDataDir->VirtualAddress is the content of origin PE file, I think it's impossible
> that the size of PE file is only 8 bytes.
> 
> As I changed the one line code in DxeImageVerificationHandler to reproduce the
> issue, I'm not sure whether it's ok.
> 
> Thanks
> Wenyi
> 
> On 2020/8/19 17:26, Laszlo Ersek wrote:
> > On 08/18/20 17:18, Mathews, John wrote:
> >> I dug up the original report details.  This was noted as a concern during a
> source code inspection.  There was no demonstration of how it might be
> triggered.
> >>
> >> " There is an integer overflow vulnerability in the
> DxeImageVerificationHandler function when
> >> parsing the PE files attribute certificate table. In cases where WinCertificate-
> >dwLength is
> >> sufficiently large, it's possible to overflow Offset back to 0 causing an endless
> loop."
> >>
> >> The recommendation was to add stricter checking of "Offset" and the
> embedded length fields of certificate data
> >> before using them.
> >
> > Thanks for checking!
> >
> > Laszlo
> >
> >>
> >>
> >>
> >> -----Original Message-----
> >> From: Laszlo Ersek <lersek@redhat.com>
> >> Sent: Tuesday, August 18, 2020 1:59 AM
> >> To: Wang, Jian J <jian.j.wang@intel.com>; devel@edk2.groups.io; Yao,
> Jiewen <jiewen.yao@intel.com>; xiewenyi2@huawei.com
> >> Cc: huangming23@huawei.com; songdongkuang@huawei.com; Mathews,
> John <john.mathews@intel.com>
> >> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> >>
> >> On 08/18/20 04:10, Wang, Jian J wrote:
> >>> Laszlo,
> >>>
> >>> My apologies for the slow response. I'm not the original reporter but
> >>> just the BZ submitter. And I didn't do deep analysis to this issue.
> >>> The issues was reported from one internal team. Add John in loop to see if
> he knows more about it or not.
> >>>
> >>> My superficial understanding on such issue is that, if there's
> >>> "potential" issue in theory and hard to reproduce, it's still worthy
> >>> of using an alternative way to replace the original implementation
> >>> with no "potential" issue at all. Maybe we don't have to prove old way is
> something wrong but must prove that the new way is really safe.
> >>
> >> I agree, thanks.
> >>
> >> It would be nice to hear more from the internal team about the originally
> reported (even if hard-to-trigger) issue.
> >>
> >> Thanks!
> >> Laszlo
> >>
> >>>
> >>> Regards,
> >>> Jian
> >>>
> >>>> -----Original Message-----
> >>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Laszlo
> >>>> Ersek
> >>>> Sent: Tuesday, August 18, 2020 12:53 AM
> >>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
> >>>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
> >>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
> >>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> >>>>
> >>>> Hi Jiewen,
> >>>>
> >>>> On 08/14/20 10:53, Yao, Jiewen wrote:
> >>>>>> To Jiewen,
> >>>>>> Sorry, I don't have environment to reproduce the issue.
> >>>>>
> >>>>> Please help me understand, if you don’t have environment to
> >>>>> reproduce the
> >>>> issue, how do you guarantee that your patch does fix the problem and
> >>>> we don’t have any other vulnerabilities?
> >>>>
> >>>> The original bug report in
> >>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is seriously
> >>>> lacking. It does not go into detail about the alleged integer overflow.
> >>>> It does not quote the code, does not explain the control flow, does
> >>>> not identify the exact edk2 commit at which the vulnerability exists.
> >>>>
> >>>> The bug report also does not offer a reproducer.
> >>>>
> >>>> Additionally, the exact statement that the bug report does make,
> >>>> namely
> >>>>
> >>>>   it's possible to overflow Offset back to 0 causing an endless loop
> >>>>
> >>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can
> >>>> be overflowed to zero, but the *addend* that is added to OffSet can
> >>>> be overflowed to zero. Therefore the infinite loop will arise because
> >>>> OffSet remains stuck at its present value, and not because OffSet
> >>>> will be re-set to zero.
> >>>>
> >>>> For the reasons, we can only speculate as to what the actual problem
> >>>> is, unless Jian decides to join the discussion and clarifies what he
> >>>> had in mind originally.
> >>>>
> >>>> My understanding (or even "reconstruction") of the vulnerability is
> >>>> described above, and in the patches that I proposed.
> >>>>
> >>>> We can write a patch based on code analysis. It's possible to
> >>>> identify integer overflows based on code analysis, and it's possible
> >>>> to verify the correctness of fixes by code review. Obviously testing
> >>>> is always good, but many times, constructing reproducers for such
> >>>> issues that were found by code review, is difficult and time
> >>>> consuming. We can say that we don't fix vulnerabilities without
> >>>> reproducers, or we can say that we make an effort to fix them even if
> >>>> all we have is code analysis (and not a reproducer).
> >>>>
> >>>> So the above paragraph concerns "correctness". Regarding
> >>>> "completeness", I guarantee you that this patch does not fix *all*
> >>>> problems related to PE parsing. (See the other BZ tickets.) It does
> >>>> fix *one* issue with PE parsing. We can say that we try to fix such
> >>>> issues gradually (give different CVE numbers to different issues, and
> >>>> address them one at a time), or we can say that we rewrite PE parsing
> from the ground up.
> >>>> (BTW: I have seriously attempted that in the past, and I gave up,
> >>>> because the PE format is FUBAR.)
> >>>>
> >>>> In summary:
> >>>>
> >>>> - the problem statement is unclear,
> >>>>
> >>>> - it seems like there is indeed an integer overflow problem in the
> >>>> SecDataDir parsing loop, but it's uncertain whether the bug reporter
> >>>> had exactly that in mind
> >>>>
> >>>> - PE parsing is guaranteed to have other vulnerabilities elsewhere in
> >>>> edk2, but I'm currently unaware of other such issues in
> >>>> DxeImageVerificationLib specifically
> >>>>
> >>>> - even if there are other such problems (in DxeImageVerificationLib
> >>>> or elswehere), fixing this bug that we know about is likely
> >>>> worthwhile
> >>>>
> >>>> - for many such bugs, constructing a reproducer is difficult and time
> >>>> consuming; code analysis, and *regression-testing* are frequently the
> >>>> only tools we have. That doesn't mean we should ignore this class of bugs.
> >>>>
> >>>> (Fixing integer overflows retro-actively is more difficult than
> >>>> writing overflow-free code in the first place, but that ship has
> >>>> sailed; so we can only fight these bugs incrementally now, unless we
> >>>> can rewrite PE parsing with a new data structure from the ground up.
> >>>> Again I tried that and gave up, because the spec is not public, and
> >>>> what I did manage to learn about PE, showed that it was insanely
> >>>> over-engineered. I'm not saying that other binary / executable
> >>>> formats are better, of course.)
> >>>>
> >>>> Please check out my patches (inlined elsewhere in this thread), and
> >>>> comment whether you'd like me to post them to the list as a
> >>>> standalone series.
> >>>>
> >>>> Jian: it wouldn't hurt if you commented as well.
> >>>>
> >>>> Thanks
> >>>> Laszlo
> >>>>
> >>>>>> -----Original Message-----
> >>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
> >>>> wenyi,xie
> >>>>>> via groups.io
> >>>>>> Sent: Friday, August 14, 2020 3:54 PM
> >>>>>> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io; Yao,
> >>>>>> Jiewen <jiewen.yao@intel.com>; Wang, Jian J <jian.j.wang@intel.com>
> >>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
> >>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> >>>>>>
> >>>>>> To Laszlo,
> >>>>>> Thank you for your detailed description, I agree with what you
> >>>>>> analyzed and
> >>>> I'm
> >>>>>> OK with your patches, it's
> >>>>>> correct and much simpler.
> >>>>>>
> >>>>>> To Jiewen,
> >>>>>> Sorry, I don't have environment to reproduce the issue.
> >>>>>>
> >>>>>> Thanks
> >>>>>> Wenyi
> >>>>>>
> >>>>>> On 2020/8/14 2:50, Laszlo Ersek wrote:
> >>>>>>> On 08/13/20 13:55, Wenyi Xie wrote:
> >>>>>>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
> >>>>>>>>
> >>>>>>>> There is an integer overflow vulnerability in
> >>>>>>>> DxeImageVerificationHandler function when parsing the PE files
> >>>>>>>> attribute certificate table. In cases where
> >>>>>>>> WinCertificate->dwLength is sufficiently large, it's possible to
> overflow Offset back to 0 causing an endless loop.
> >>>>>>>>
> >>>>>>>> Check offset inbetween VirtualAddress and VirtualAddress + Size.
> >>>>>>>> Using SafeintLib to do offset addition with result check.
> >>>>>>>>
> >>>>>>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
> >>>>>>>> Cc: Jian J Wang <jian.j.wang@intel.com>
> >>>>>>>> Cc: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
> >>>>>>>> ---
> >>>>>>>>
> >>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.inf
> >>>> |
> >>>>>> 1 +
> >>>>>>>>
> >>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.h
> >>>> |
> >>>>>> 1 +
> >>>>>>>>
> >>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.c
> >>>> |
> >>>>>> 111 +++++++++++---------
> >>>>>>>>  3 files changed, 63 insertions(+), 50 deletions(-)
> >>>>>>>>
> >>>>>>>> diff --git
> >>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.inf
> >>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.inf
> >>>>>>>> index 1e1a639857e0..a7ac4830b3d4 100644
> >>>>>>>> ---
> >>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.inf
> >>>>>>>> +++
> >>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.inf
> >>>>>>>> @@ -53,6 +53,7 @@ [LibraryClasses]
> >>>>>>>>    SecurityManagementLib
> >>>>>>>>    PeCoffLib
> >>>>>>>>    TpmMeasurementLib
> >>>>>>>> +  SafeIntLib
> >>>>>>>>
> >>>>>>>>  [Protocols]
> >>>>>>>>    gEfiFirmwareVolume2ProtocolGuid       ## SOMETIMES_CONSUMES
> >>>>>>>> diff --git
> >>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.h
> >>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.h
> >>>>>>>> index 17955ff9774c..060273917d5d 100644
> >>>>>>>> ---
> >>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.h
> >>>>>>>> +++
> >>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.h
> >>>>>>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
> >>>>>>>> #include <Library/DevicePathLib.h>  #include
> >>>>>>>> <Library/SecurityManagementLib.h>  #include <Library/PeCoffLib.h>
> >>>>>>>> +#include <Library/SafeIntLib.h>
> >>>>>>>>  #include <Protocol/FirmwareVolume2.h>  #include
> >>>>>>>> <Protocol/DevicePath.h>  #include <Protocol/BlockIo.h> diff --git
> >>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.c
> >>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.c
> >>>>>>>> index 36b87e16d53d..dbc03e28c05b 100644
> >>>>>>>> ---
> >>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
> >>>> .c
> >>>>>>>> +++
> >>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.c
> >>>>>>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
> >>>>>>>>    EFI_STATUS                           HashStatus;
> >>>>>>>>    EFI_STATUS                           DbStatus;
> >>>>>>>>    BOOLEAN                              IsFound;
> >>>>>>>> +  UINT32                               AlignedLength;
> >>>>>>>> +  UINT32                               Result;
> >>>>>>>> +  EFI_STATUS                           AddStatus;
> >>>>>>>> +  BOOLEAN                              IsAuthDataAssigned;
> >>>>>>>>
> >>>>>>>>    SignatureList     = NULL;
> >>>>>>>>    SignatureListSize = 0;
> >>>>>>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
> >>>>>>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
> >>>>>>>>    IsVerified        = FALSE;
> >>>>>>>>    IsFound           = FALSE;
> >>>>>>>> +  Result            = 0;
> >>>>>>>>
> >>>>>>>>    //
> >>>>>>>>    // Check the image type and get policy setting.
> >>>>>>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
> >>>>>>>>    // The first certificate starts at offset
> >>>>>>>> (SecDataDir->VirtualAddress) from
> >>>> the
> >>>>>> start of the file.
> >>>>>>>>    //
> >>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>>>>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> (WinCertificate-
> >>>>>>> dwLength))) {
> >>>>>>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet <
> >>>>>>>> + (SecDataDir-
> >>>>>>> VirtualAddress + SecDataDir->Size));) {
> >>>>>>>> +    IsAuthDataAssigned = FALSE;
> >>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE
> >>>> (WinCertificate-
> >>>>>>> dwLength);
> >>>>>>>
> >>>>>>> I disagree with this patch.
> >>>>>>>
> >>>>>>> The primary reason for my disagreement is that the bug report
> >>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is
> >>>>>>> inexact, and so this patch tries to fix the wrong thing.
> >>>>>>>
> >>>>>>> With edk2 master at commit 65904cdbb33c, it is *not* possible to
> >>>>>>> overflow the OffSet variable to zero with "WinCertificate->dwLength"
> >>>>>>> *purely*, and cause an endless loop. Note that we have (at commit
> >>>>>>> 65904cdbb33c):
> >>>>>>>
> >>>>>>>   for (OffSet = SecDataDir->VirtualAddress;
> >>>>>>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>>>>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >>>>>>> (WinCertificate-
> >>>>>>> dwLength))) {
> >>>>>>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
> >>>>>>> <= sizeof
> >>>>>> (WIN_CERTIFICATE) ||
> >>>>>>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> >>>> WinCertificate-
> >>>>>>> dwLength) {
> >>>>>>>       break;
> >>>>>>>     }
> >>>>>>>
> >>>>>>> The last sub-condition checks whether the Security Data Directory
> >>>>>>> has enough room left for "WinCertificate->dwLength". If not, then
> >>>>>>> we break out of the loop.
> >>>>>>>
> >>>>>>> If we *do* have enough room, that is:
> >>>>>>>
> >>>>>>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >=
> >>>> WinCertificate-
> >>>>>>> dwLength
> >>>>>>>
> >>>>>>> then we have (by adding OffSet to both sides):
> >>>>>>>
> >>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet +
> >>>>>>> WinCertificate- dwLength
> >>>>>>>
> >>>>>>> The left hand side is a known-good UINT32, and so incrementing
> >>>>>>> OffSet (a
> >>>>>>> UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32)
> >>>>>>> does not cause an overflow.
> >>>>>>>
> >>>>>>> Instead, the problem is with the alignment. The "if" statement
> >>>>>>> checks whether we have enough room for "dwLength", but then
> >>>>>>> "OffSet" is advanced by "dwLength" *aligned up* to the next
> >>>>>>> multiple of 8. And that may indeed cause various overflows.
> >>>>>>>
> >>>>>>> Now, the main problem with the present patch is that it does not
> >>>>>>> fix one of those overflows. Namely, consider that "dwLength" is
> >>>>>>> very close to
> >>>>>>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then aligning
> >>>>>>> it up to the next multiple of 8 will yield 0. In other words,
> "AlignedLength"
> >>>>>>> will be zero.
> >>>>>>>
> >>>>>>> And when that happens, there's going to be an infinite loop just
> >>>>>>> the
> >>>>>>> same: "OffSet" will not be zero, but it will be *stuck*. The
> >>>>>>> SafeUint32Add() call at the bottom will succeed, but it will not
> >>>>>>> change the value of "OffSet".
> >>>>>>>
> >>>>>>> More at the bottom.
> >>>>>>>
> >>>>>>>
> >>>>>>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
> >>>>>>>> <= sizeof
> >>>>>> (WIN_CERTIFICATE) ||
> >>>>>>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
> >>>>>>>> <
> >>>>>> WinCertificate->dwLength) {
> >>>>>>>>        break;
> >>>>>>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
> >>>>>>>>        }
> >>>>>>>>        AuthData   = PkcsCertData->CertData;
> >>>>>>>>        AuthDataSize = PkcsCertData->Hdr.dwLength -
> >>>>>>>> sizeof(PkcsCertData-
> >>>>> Hdr);
> >>>>>>>> +      IsAuthDataAssigned = TRUE;
> >>>>>>>> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> >>>>>>>>      } else if (WinCertificate->wCertificateType ==
> >>>> WIN_CERT_TYPE_EFI_GUID)
> >>>>>> {
> >>>>>>>>        //
> >>>>>>>>        // The certificate is formatted as
> >>>>>>>> WIN_CERTIFICATE_UEFI_GUID which
> >>>> is
> >>>>>> described in UEFI Spec.
> >>>>>>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
> >>>>>>>>        if (WinCertUefiGuid->Hdr.dwLength <=
> >>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
> >>>>>>>>          break;
> >>>>>>>>        }
> >>>>>>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType,
> &gEfiCertPkcs7Guid))
> >>>> {
> >>>>>>>> -        continue;
> >>>>>>>> +      if (CompareGuid (&WinCertUefiGuid->CertType,
> >>>>>>>> + &gEfiCertPkcs7Guid))
> >>>> {
> >>>>>>>> +        AuthData = WinCertUefiGuid->CertData;
> >>>>>>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
> >>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
> >>>>>>>> +        IsAuthDataAssigned = TRUE;
> >>>>>>>> +        HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> >>>>>>>>        }
> >>>>>>>> -      AuthData = WinCertUefiGuid->CertData;
> >>>>>>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
> >>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
> >>>>>>>>      } else {
> >>>>>>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
> >>>>>>>>          break;
> >>>>>>>>        }
> >>>>>>>> -      continue;
> >>>>>>>>      }
> >>>>>>>>
> >>>>>>>> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> >>>>>>>> -    if (EFI_ERROR (HashStatus)) {
> >>>>>>>> -      continue;
> >>>>>>>> -    }
> >>>>>>>> -
> >>>>>>>> -    //
> >>>>>>>> -    // Check the digital signature against the revoked certificate in
> >>>> forbidden
> >>>>>> database (dbx).
> >>>>>>>> -    //
> >>>>>>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> >>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> >>>>>>>> -      IsVerified = FALSE;
> >>>>>>>> -      break;
> >>>>>>>> -    }
> >>>>>>>> -
> >>>>>>>> -    //
> >>>>>>>> -    // Check the digital signature against the valid certificate in
> allowed
> >>>>>> database (db).
> >>>>>>>> -    //
> >>>>>>>> -    if (!IsVerified) {
> >>>>>>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
> >>>>>>>> -        IsVerified = TRUE;
> >>>>>>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
> >>>>>>>> +      //
> >>>>>>>> +      // Check the digital signature against the revoked
> >>>>>>>> + certificate in
> >>>> forbidden
> >>>>>> database (dbx).
> >>>>>>>> +      //
> >>>>>>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> >>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> >>>>>>>> +        IsVerified = FALSE;
> >>>>>>>> +        break;
> >>>>>>>>        }
> >>>>>>>> -    }
> >>>>>>>>
> >>>>>>>> -    //
> >>>>>>>> -    // Check the image's hash value.
> >>>>>>>> -    //
> >>>>>>>> -    DbStatus = IsSignatureFoundInDatabase (
> >>>>>>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
> >>>>>>>> -                 mImageDigest,
> >>>>>>>> -                 &mCertType,
> >>>>>>>> -                 mImageDigestSize,
> >>>>>>>> -                 &IsFound
> >>>>>>>> -                 );
> >>>>>>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
> >>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> >>>>>>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
> signed
> >>>> but %s
> >>>>>> hash of image is found in DBX.\n", mHashTypeStr));
> >>>>>>>> -      IsVerified = FALSE;
> >>>>>>>> -      break;
> >>>>>>>> -    }
> >>>>>>>> +      //
> >>>>>>>> +      // Check the digital signature against the valid
> >>>>>>>> + certificate in allowed
> >>>>>> database (db).
> >>>>>>>> +      //
> >>>>>>>> +      if (!IsVerified) {
> >>>>>>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
> >>>>>>>> +          IsVerified = TRUE;
> >>>>>>>> +        }
> >>>>>>>> +      }
> >>>>>>>>
> >>>>>>>> -    if (!IsVerified) {
> >>>>>>>> +      //
> >>>>>>>> +      // Check the image's hash value.
> >>>>>>>> +      //
> >>>>>>>>        DbStatus = IsSignatureFoundInDatabase (
> >>>>>>>> -                   EFI_IMAGE_SECURITY_DATABASE,
> >>>>>>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
> >>>>>>>>                     mImageDigest,
> >>>>>>>>                     &mCertType,
> >>>>>>>>                     mImageDigestSize,
> >>>>>>>>                     &IsFound
> >>>>>>>>                     );
> >>>>>>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
> >>>>>>>> -        IsVerified = TRUE;
> >>>>>>>> -      } else {
> >>>>>>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
> signed
> >>>> but
> >>>>>> signature is not allowed by DB and %s hash of image is not found in
> >>>> DB/DBX.\n",
> >>>>>> mHashTypeStr));
> >>>>>>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
> >>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> >>>>>>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
> >>>>>>>> + signed
> >>>>>> but %s hash of image is found in DBX.\n", mHashTypeStr));
> >>>>>>>> +        IsVerified = FALSE;
> >>>>>>>> +        break;
> >>>>>>>>        }
> >>>>>>>> +
> >>>>>>>> +      if (!IsVerified) {
> >>>>>>>> +        DbStatus = IsSignatureFoundInDatabase (
> >>>>>>>> +                     EFI_IMAGE_SECURITY_DATABASE,
> >>>>>>>> +                     mImageDigest,
> >>>>>>>> +                     &mCertType,
> >>>>>>>> +                     mImageDigestSize,
> >>>>>>>> +                     &IsFound
> >>>>>>>> +                     );
> >>>>>>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
> >>>>>>>> +          IsVerified = TRUE;
> >>>>>>>> +        } else {
> >>>>>>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
> >>>>>>>> + signed
> >>>> but
> >>>>>> signature is not allowed by DB and %s hash of image is not found in
> >>>> DB/DBX.\n",
> >>>>>> mHashTypeStr));
> >>>>>>>> +        }
> >>>>>>>> +      }
> >>>>>>>> +    }
> >>>>>>>> +
> >>>>>>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
> >>>>>>>> +    if (EFI_ERROR (AddStatus)) {
> >>>>>>>> +      break;
> >>>>>>>>      }
> >>>>>>>> +    OffSet = Result;
> >>>>>>>>    }
> >>>>>>>>
> >>>>>>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
> >>>>>>>> {
> >>>>>>>>
> >>>>>>>
> >>>>>>> There are other (smaller) reasons why I dislike this patch:
> >>>>>>>
> >>>>>>> - The "IsAuthDataAssigned" variable is superfluous; we could use
> >>>>>>> the existent "AuthData" variable (with a NULL-check and a
> >>>>>>> NULL-assignment) similarly.
> >>>>>>>
> >>>>>>> - The patch complicates / reorganizes the control flow needlessly.
> >>>>>>> This complication originates from placing the checked "OffSet"
> >>>>>>> increment at the bottom of the loop, which then requires the
> >>>>>>> removal of all the "continue" statements. But we don't need to
> >>>>>>> check-and-increment at the bottom. We can keep the increment
> >>>>>>> inside the "for" statement, only extend the *existent* room check
> >>>>>>> (which I've quoted) to take the alignment into account as well. If
> >>>>>>> there is enough room for the alignment in the security data
> >>>>>>> directory, then that guarantees there won't be a UINT32 overflow
> either.
> >>>>>>>
> >>>>>>> All in all, I'm proposing the following three patches instead. The
> >>>>>>> first two patches are preparation, the last patch is the fix.
> >>>>>>>
> >>>>>>> Patch#1:
> >>>>>>>
> >>>>>>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep 17
> >>>> 00:00:00
> >>>>>> 2001
> >>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
> >>>>>>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib: extract
> >>>>>>>> SecDataDirEnd, SecDataDirLeft
> >>>>>>>>
> >>>>>>>> The following two quantities:
> >>>>>>>>
> >>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
> >>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
> >>>>>>>>
> >>>>>>>> are used multiple times in DxeImageVerificationHandler().
> >>>>>>>> Introduce helper variables for them: "SecDataDirEnd" and
> "SecDataDirLeft", respectively.
> >>>>>>>> This saves us multiple calculations and significantly simplifies the code.
> >>>>>>>>
> >>>>>>>> Note that all three summands above have type UINT32, therefore
> >>>>>>>> the new variables are also of type UINT32.
> >>>>>>>>
> >>>>>>>> This patch does not change behavior.
> >>>>>>>>
> >>>>>>>> (Note that the code already handles the case when the
> >>>>>>>>
> >>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
> >>>>>>>>
> >>>>>>>> UINT32 addition overflows -- namely, in that case, the
> >>>>>>>> certificate loop is never entered, and the corruption check right
> >>>>>>>> after the loop fires.)
> >>>>>>>>
> >>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>> ---
> >>>>>>>>
> >>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.c |
> >>>> 12
> >>>>>> ++++++++----
> >>>>>>>>  1 file changed, 8 insertions(+), 4 deletions(-)
> >>>>>>>>
> >>>>>>>> diff --git
> >>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.c
> >>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.c
> >>>>>>>> index 36b87e16d53d..8761980c88aa 100644
> >>>>>>>> ---
> >>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
> >>>> .c
> >>>>>>>> +++
> >>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.c
> >>>>>>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
> >>>>>>>>    UINT8                                *AuthData;
> >>>>>>>>    UINTN                                AuthDataSize;
> >>>>>>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
> >>>>>>>> +  UINT32                               SecDataDirEnd;
> >>>>>>>> +  UINT32                               SecDataDirLeft;
> >>>>>>>>    UINT32                               OffSet;
> >>>>>>>>    CHAR16                               *NameStr;
> >>>>>>>>    RETURN_STATUS                        PeCoffStatus;
> >>>>>>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
> >>>>>>>>    // "Attribute Certificate Table".
> >>>>>>>>    // The first certificate starts at offset
> >>>>>>>> (SecDataDir->VirtualAddress) from
> >>>> the
> >>>>>> start of the file.
> >>>>>>>>    //
> >>>>>>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
> >>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>>>>>> +       OffSet < SecDataDirEnd;
> >>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >>>>>>>> (WinCertificate-
> >>>>>>> dwLength))) {
> >>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <=
> sizeof
> >>>>>> (WIN_CERTIFICATE) ||
> >>>>>>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> >>>>>> WinCertificate->dwLength) {
> >>>>>>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
> >>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
> >>>>>>>> +        SecDataDirLeft < WinCertificate->dwLength) {
> >>>>>>>>        break;
> >>>>>>>>      }
> >>>>>>>>
> >>>>>>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
> >>>>>>>>      }
> >>>>>>>>    }
> >>>>>>>>
> >>>>>>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
> >>>>>>>> {
> >>>>>>>> +  if (OffSet != SecDataDirEnd) {
> >>>>>>>>      //
> >>>>>>>>      // The Size in Certificate Table or the attribute
> >>>>>>>> certificate table is
> >>>> corrupted.
> >>>>>>>>      //
> >>>>>>>> --
> >>>>>>>> 2.19.1.3.g30247aa5d201
> >>>>>>>>
> >>>>>>>
> >>>>>>> Patch#2:
> >>>>>>>
> >>>>>>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep 17
> 00:00:00
> >>>>>> 2001
> >>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
> >>>>>>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib: assign
> >>>>>>>> WinCertificate after size check
> >>>>>>>>
> >>>>>>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) check
> >>>>>>>> only guards the de-referencing of the "WinCertificate" pointer.
> >>>>>>>> It does not guard the calculation of hte pointer itself:
> >>>>>>>>
> >>>>>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>>
> >>>>>>>> This is wrong; if we don't know for sure that we have enough room
> >>>>>>>> for a WIN_CERTIFICATE, then even creating such a pointer, not
> >>>>>>>> just de-referencing it, may invoke undefined behavior.
> >>>>>>>>
> >>>>>>>> Move the pointer calculation after the size check.
> >>>>>>>>
> >>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>> ---
> >>>>>>>>
> >>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.c |
> >>>> 8
> >>>>>> +++++---
> >>>>>>>>  1 file changed, 5 insertions(+), 3 deletions(-)
> >>>>>>>>
> >>>>>>>> diff --git
> >>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.c
> >>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.c
> >>>>>>>> index 8761980c88aa..461ed7cfb5ac 100644
> >>>>>>>> ---
> >>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
> >>>> .c
> >>>>>>>> +++
> >>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.c
> >>>>>>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
> >>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>>>>>>         OffSet < SecDataDirEnd;
> >>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >>>>>>>> (WinCertificate-
> >>>>>>> dwLength))) {
> >>>>>>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
> >>>>>>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
> >>>>>>>> -        SecDataDirLeft < WinCertificate->dwLength) {
> >>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
> >>>>>>>> +      break;
> >>>>>>>> +    }
> >>>>>>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
> >>>>>>>>        break;
> >>>>>>>>      }
> >>>>>>>>
> >>>>>>>> --
> >>>>>>>> 2.19.1.3.g30247aa5d201
> >>>>>>>>
> >>>>>>>
> >>>>>>> Patch#3:
> >>>>>>>
> >>>>>>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17
> 00:00:00
> >>>>>> 2001
> >>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
> >>>>>>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch
> >>>> alignment
> >>>>>>>>  overflow (CVE-2019-14562)
> >>>>>>>>
> >>>>>>>> The DxeImageVerificationHandler() function currently checks
> >>>>>>>> whether "SecDataDir" has enough room for
> >>>>>>>> "WinCertificate->dwLength". However,
> >>>>>> for
> >>>>>>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to the
> >>>>>>>> next multiple of 8. If "WinCertificate->dwLength" is large
> >>>>>>>> enough, the alignment will return 0, and "OffSet" will be stuck at the
> same value.
> >>>>>>>>
> >>>>>>>> Check whether "SecDataDir" has room left for both
> >>>>>>>> "WinCertificate->dwLength" and the alignment.
> >>>>>>>>
> >>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>> ---
> >>>>>>>>
> >>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.c |
> >>>> 4
> >>>>>> +++-
> >>>>>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
> >>>>>>>>
> >>>>>>>> diff --git
> >>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.c
> >>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.c
> >>>>>>>> index 461ed7cfb5ac..e38eb981b7a0 100644
> >>>>>>>> ---
> >>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
> >>>> .c
> >>>>>>>> +++
> >>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>> ib.c
> >>>>>>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
> >>>>>>>>        break;
> >>>>>>>>      }
> >>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
> >>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
> >>>>>>>> +        (SecDataDirLeft - WinCertificate->dwLength <
> >>>>>>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
> >>>>>>>>        break;
> >>>>>>>>      }
> >>>>>>>>
> >>>>>>>> --
> >>>>>>>> 2.19.1.3.g30247aa5d201
> >>>>>>>>
> >>>>>>>
> >>>>>>> If Wenyi and the reviewers are OK with these patches, I can submit
> >>>>>>> them as a standalone patch series.
> >>>>>>>
> >>>>>>> Note that I do not have any reproducer for the issue; the best
> >>>>>>> testing that I could offer would be some light-weight Secure Boot
> >>>>>>> regression tests.
> >>>>>>>
> >>>>>>> Thanks
> >>>>>>> Laszlo
> >>>>>>>
> >>>>>>>
> >>>>>>> .
> >>>>>>>
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>>
> >>>>
> >>>>
> >>>>
> >>>
> >>
> >
> >
> > .
> >
> 
> 
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-28  3:50                     ` Yao, Jiewen
@ 2020-08-28  6:18                       ` wenyi,xie
  2020-08-28  6:43                         ` Yao, Jiewen
  0 siblings, 1 reply; 32+ messages in thread
From: wenyi,xie @ 2020-08-28  6:18 UTC (permalink / raw)
  To: Yao, Jiewen, devel@edk2.groups.io, Laszlo Ersek, Wang, Jian J
  Cc: songdongkuang@huawei.com, Mathews, John

Hi,Jiewen,

I don't really get the meaning "create a case that bypass all checks in PeCoffLib", do you mean I need to create a PE file that can bypass all check in PeCoffLib without modify any
code and then cause the problem in DxeImageVerificationLib, or just modify some code in PeCoffLib to bypass check instead of removing the calling of PeCoffLoaderGetImageInfo. Would
you mind explaining a little more specifically? As far as I tried, it's really hard to reproduce the issue without touching any code.

Thanks
Wenyi

On 2020/8/28 11:50, Yao, Jiewen wrote:
> HI Wenyi
> Thank you very much to take time to reproduce.
> 
> I am particular interested in below:
> 	"As PE file is modified, function PeCoffLoaderGetImageInfo will return error, so I have to remove it so that for loop can be tested in DxeImageVerificationHandler."
> 
> By design, the PeCoffLib should catch illegal PE/COFF image and return error. (even it cannot catch all, it should catch most ones).
> Other PE/COFF parser may rely on the checker in PeCoffLib and *no need* to duplicate all checkers.
> As such, DxeImageVerificationLib (and other PeCoff consumer) just need checks what has passed the check in PeCoffLib.
> 
> I don’t think you should remove the checker. If people can remove the checker, I am sure the rest code will be vulnerable, according to the current design.
> Could you please to create a case that bypass all checks in PeCoffLib, then run into DxeImageVerificationLib and cause the problem? That would be more valuable for us.
> 
> Thank you
> Yao Jiewen
> 
>> -----Original Message-----
>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of wenyi,xie
>> via groups.io
>> Sent: Friday, August 28, 2020 11:18 AM
>> To: Laszlo Ersek <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>;
>> devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>
>> Cc: songdongkuang@huawei.com; Mathews, John <john.mathews@intel.com>
>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>
>> Hi,Laszlo and everyone,
>>
>> These days I tried to reproduce the issue,and made some progress. I think
>> there are two way to cause overflow from a mathematical point of view,
>> 1.As Laszlo analysed before, if WinCertificate->dwLength is large enough, close
>> to MAX_UINT32, then (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>> dwLength)) will cause overflow.
>> 2.(WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength)) is good,
>> OffSet is good, but OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>> (WinCertificate->dwLength)) cause overflow.
>>
>> Here I choose the second way to reproduce the issue, I choose a PE file and sign
>> it with my own db certificate. Then I use binary edit tool to modify the PE file like
>> below,
>>
>> 1.change SecDataDir->Size from 0x5F8 to 0xFFFF1FFF
>> 2.change WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFE
>> SecDataDir->VirtualAddress in my PE is 0xe000 and no need to change.
>>
>> As PE file is modified, function PeCoffLoaderGetImageInfo will return error, so I
>> have to remove it so that for loop can be tested in DxeImageVerificationHandler.
>> The log is as below,
>>
>> SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFF.
>> (First Loop)
>> OffSet=0xE000.
>> WinCertificate->dwLength=0xFFFF1FFE, ALIGN_SIZE (WinCertificate-
>>> dwLength)=0x2.
>> (Second Loop)
>> OffSet=0x0.
>> WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate-
>>> dwLength)=0x3.
>> (Third Loop)
>> OffSet=0x5A50.
>> WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate-
>>> dwLength)=0x4.
>> (Forth Loop)
>> OffSet=0xEA78.
>> WinCertificate->dwLength=0xAFAFAFAF, ALIGN_SIZE (WinCertificate-
>>> dwLength)=0x1.
>> (Fifth Loop)
>> OffSet=0xAFB09A28.
>>
>> As I modify SecDataDir->Size and WinCertificate->dwLength, so in first loop the
>> condition check whether the Security Data Directory has enough room left for
>> "WinCertificate->dwLength" is ok.
>>
>> if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>> (WIN_CERTIFICATE) ||
>>     (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
>>> dwLength) {
>>   break;
>> }
>>
>> In the beginning of second loop, WinCertificate->dwLength + ALIGN_SIZE
>> (WinCertificate->dwLength) is 0xFFFF2000, offset is 0xE000
>>
>> OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))
>>
>> Offset now is 0 and overflow happens. So even if my PE only have one signature,
>> the for loop is still going untill exception happens.
>>
>>
>> I can't reproduce the issue using the first way, because if WinCertificate-
>>> dwLength is close to MAX_UINT32, it means SecDataDir->Size should also close
>> to MAX_UINT32, or the condition check
>> "(SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
>>> dwLength" will break. But if SecDataDir->Size is very large, SecDataDir-
>>> VirtualAddress have to be smaller than 8 bytes,
>> because SecDataDir->VirtualAddress + SecDataDir->Size < MAX_UINT32.
>> SecDataDir->VirtualAddress is the beginning address of the signature, and before
>> SecDataDir->VirtualAddress is the content of origin PE file, I think it's impossible
>> that the size of PE file is only 8 bytes.
>>
>> As I changed the one line code in DxeImageVerificationHandler to reproduce the
>> issue, I'm not sure whether it's ok.
>>
>> Thanks
>> Wenyi
>>
>> On 2020/8/19 17:26, Laszlo Ersek wrote:
>>> On 08/18/20 17:18, Mathews, John wrote:
>>>> I dug up the original report details.  This was noted as a concern during a
>> source code inspection.  There was no demonstration of how it might be
>> triggered.
>>>>
>>>> " There is an integer overflow vulnerability in the
>> DxeImageVerificationHandler function when
>>>> parsing the PE files attribute certificate table. In cases where WinCertificate-
>>> dwLength is
>>>> sufficiently large, it's possible to overflow Offset back to 0 causing an endless
>> loop."
>>>>
>>>> The recommendation was to add stricter checking of "Offset" and the
>> embedded length fields of certificate data
>>>> before using them.
>>>
>>> Thanks for checking!
>>>
>>> Laszlo
>>>
>>>>
>>>>
>>>>
>>>> -----Original Message-----
>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>> Sent: Tuesday, August 18, 2020 1:59 AM
>>>> To: Wang, Jian J <jian.j.wang@intel.com>; devel@edk2.groups.io; Yao,
>> Jiewen <jiewen.yao@intel.com>; xiewenyi2@huawei.com
>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com; Mathews,
>> John <john.mathews@intel.com>
>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>
>>>> On 08/18/20 04:10, Wang, Jian J wrote:
>>>>> Laszlo,
>>>>>
>>>>> My apologies for the slow response. I'm not the original reporter but
>>>>> just the BZ submitter. And I didn't do deep analysis to this issue.
>>>>> The issues was reported from one internal team. Add John in loop to see if
>> he knows more about it or not.
>>>>>
>>>>> My superficial understanding on such issue is that, if there's
>>>>> "potential" issue in theory and hard to reproduce, it's still worthy
>>>>> of using an alternative way to replace the original implementation
>>>>> with no "potential" issue at all. Maybe we don't have to prove old way is
>> something wrong but must prove that the new way is really safe.
>>>>
>>>> I agree, thanks.
>>>>
>>>> It would be nice to hear more from the internal team about the originally
>> reported (even if hard-to-trigger) issue.
>>>>
>>>> Thanks!
>>>> Laszlo
>>>>
>>>>>
>>>>> Regards,
>>>>> Jian
>>>>>
>>>>>> -----Original Message-----
>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Laszlo
>>>>>> Ersek
>>>>>> Sent: Tuesday, August 18, 2020 12:53 AM
>>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
>>>>>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>
>>>>>> Hi Jiewen,
>>>>>>
>>>>>> On 08/14/20 10:53, Yao, Jiewen wrote:
>>>>>>>> To Jiewen,
>>>>>>>> Sorry, I don't have environment to reproduce the issue.
>>>>>>>
>>>>>>> Please help me understand, if you don’t have environment to
>>>>>>> reproduce the
>>>>>> issue, how do you guarantee that your patch does fix the problem and
>>>>>> we don’t have any other vulnerabilities?
>>>>>>
>>>>>> The original bug report in
>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is seriously
>>>>>> lacking. It does not go into detail about the alleged integer overflow.
>>>>>> It does not quote the code, does not explain the control flow, does
>>>>>> not identify the exact edk2 commit at which the vulnerability exists.
>>>>>>
>>>>>> The bug report also does not offer a reproducer.
>>>>>>
>>>>>> Additionally, the exact statement that the bug report does make,
>>>>>> namely
>>>>>>
>>>>>>   it's possible to overflow Offset back to 0 causing an endless loop
>>>>>>
>>>>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can
>>>>>> be overflowed to zero, but the *addend* that is added to OffSet can
>>>>>> be overflowed to zero. Therefore the infinite loop will arise because
>>>>>> OffSet remains stuck at its present value, and not because OffSet
>>>>>> will be re-set to zero.
>>>>>>
>>>>>> For the reasons, we can only speculate as to what the actual problem
>>>>>> is, unless Jian decides to join the discussion and clarifies what he
>>>>>> had in mind originally.
>>>>>>
>>>>>> My understanding (or even "reconstruction") of the vulnerability is
>>>>>> described above, and in the patches that I proposed.
>>>>>>
>>>>>> We can write a patch based on code analysis. It's possible to
>>>>>> identify integer overflows based on code analysis, and it's possible
>>>>>> to verify the correctness of fixes by code review. Obviously testing
>>>>>> is always good, but many times, constructing reproducers for such
>>>>>> issues that were found by code review, is difficult and time
>>>>>> consuming. We can say that we don't fix vulnerabilities without
>>>>>> reproducers, or we can say that we make an effort to fix them even if
>>>>>> all we have is code analysis (and not a reproducer).
>>>>>>
>>>>>> So the above paragraph concerns "correctness". Regarding
>>>>>> "completeness", I guarantee you that this patch does not fix *all*
>>>>>> problems related to PE parsing. (See the other BZ tickets.) It does
>>>>>> fix *one* issue with PE parsing. We can say that we try to fix such
>>>>>> issues gradually (give different CVE numbers to different issues, and
>>>>>> address them one at a time), or we can say that we rewrite PE parsing
>> from the ground up.
>>>>>> (BTW: I have seriously attempted that in the past, and I gave up,
>>>>>> because the PE format is FUBAR.)
>>>>>>
>>>>>> In summary:
>>>>>>
>>>>>> - the problem statement is unclear,
>>>>>>
>>>>>> - it seems like there is indeed an integer overflow problem in the
>>>>>> SecDataDir parsing loop, but it's uncertain whether the bug reporter
>>>>>> had exactly that in mind
>>>>>>
>>>>>> - PE parsing is guaranteed to have other vulnerabilities elsewhere in
>>>>>> edk2, but I'm currently unaware of other such issues in
>>>>>> DxeImageVerificationLib specifically
>>>>>>
>>>>>> - even if there are other such problems (in DxeImageVerificationLib
>>>>>> or elswehere), fixing this bug that we know about is likely
>>>>>> worthwhile
>>>>>>
>>>>>> - for many such bugs, constructing a reproducer is difficult and time
>>>>>> consuming; code analysis, and *regression-testing* are frequently the
>>>>>> only tools we have. That doesn't mean we should ignore this class of bugs.
>>>>>>
>>>>>> (Fixing integer overflows retro-actively is more difficult than
>>>>>> writing overflow-free code in the first place, but that ship has
>>>>>> sailed; so we can only fight these bugs incrementally now, unless we
>>>>>> can rewrite PE parsing with a new data structure from the ground up.
>>>>>> Again I tried that and gave up, because the spec is not public, and
>>>>>> what I did manage to learn about PE, showed that it was insanely
>>>>>> over-engineered. I'm not saying that other binary / executable
>>>>>> formats are better, of course.)
>>>>>>
>>>>>> Please check out my patches (inlined elsewhere in this thread), and
>>>>>> comment whether you'd like me to post them to the list as a
>>>>>> standalone series.
>>>>>>
>>>>>> Jian: it wouldn't hurt if you commented as well.
>>>>>>
>>>>>> Thanks
>>>>>> Laszlo
>>>>>>
>>>>>>>> -----Original Message-----
>>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>>>>>> wenyi,xie
>>>>>>>> via groups.io
>>>>>>>> Sent: Friday, August 14, 2020 3:54 PM
>>>>>>>> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io; Yao,
>>>>>>>> Jiewen <jiewen.yao@intel.com>; Wang, Jian J <jian.j.wang@intel.com>
>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>>>
>>>>>>>> To Laszlo,
>>>>>>>> Thank you for your detailed description, I agree with what you
>>>>>>>> analyzed and
>>>>>> I'm
>>>>>>>> OK with your patches, it's
>>>>>>>> correct and much simpler.
>>>>>>>>
>>>>>>>> To Jiewen,
>>>>>>>> Sorry, I don't have environment to reproduce the issue.
>>>>>>>>
>>>>>>>> Thanks
>>>>>>>> Wenyi
>>>>>>>>
>>>>>>>> On 2020/8/14 2:50, Laszlo Ersek wrote:
>>>>>>>>> On 08/13/20 13:55, Wenyi Xie wrote:
>>>>>>>>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
>>>>>>>>>>
>>>>>>>>>> There is an integer overflow vulnerability in
>>>>>>>>>> DxeImageVerificationHandler function when parsing the PE files
>>>>>>>>>> attribute certificate table. In cases where
>>>>>>>>>> WinCertificate->dwLength is sufficiently large, it's possible to
>> overflow Offset back to 0 causing an endless loop.
>>>>>>>>>>
>>>>>>>>>> Check offset inbetween VirtualAddress and VirtualAddress + Size.
>>>>>>>>>> Using SafeintLib to do offset addition with result check.
>>>>>>>>>>
>>>>>>>>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>>>>>>>>>> Cc: Jian J Wang <jian.j.wang@intel.com>
>>>>>>>>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
>>>>>>>>>> ---
>>>>>>>>>>
>>>>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.inf
>>>>>> |
>>>>>>>> 1 +
>>>>>>>>>>
>>>>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.h
>>>>>> |
>>>>>>>> 1 +
>>>>>>>>>>
>>>>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.c
>>>>>> |
>>>>>>>> 111 +++++++++++---------
>>>>>>>>>>  3 files changed, 63 insertions(+), 50 deletions(-)
>>>>>>>>>>
>>>>>>>>>> diff --git
>>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.inf
>>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.inf
>>>>>>>>>> index 1e1a639857e0..a7ac4830b3d4 100644
>>>>>>>>>> ---
>>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.inf
>>>>>>>>>> +++
>>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.inf
>>>>>>>>>> @@ -53,6 +53,7 @@ [LibraryClasses]
>>>>>>>>>>    SecurityManagementLib
>>>>>>>>>>    PeCoffLib
>>>>>>>>>>    TpmMeasurementLib
>>>>>>>>>> +  SafeIntLib
>>>>>>>>>>
>>>>>>>>>>  [Protocols]
>>>>>>>>>>    gEfiFirmwareVolume2ProtocolGuid       ## SOMETIMES_CONSUMES
>>>>>>>>>> diff --git
>>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.h
>>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.h
>>>>>>>>>> index 17955ff9774c..060273917d5d 100644
>>>>>>>>>> ---
>>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.h
>>>>>>>>>> +++
>>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.h
>>>>>>>>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
>>>>>>>>>> #include <Library/DevicePathLib.h>  #include
>>>>>>>>>> <Library/SecurityManagementLib.h>  #include <Library/PeCoffLib.h>
>>>>>>>>>> +#include <Library/SafeIntLib.h>
>>>>>>>>>>  #include <Protocol/FirmwareVolume2.h>  #include
>>>>>>>>>> <Protocol/DevicePath.h>  #include <Protocol/BlockIo.h> diff --git
>>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.c
>>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.c
>>>>>>>>>> index 36b87e16d53d..dbc03e28c05b 100644
>>>>>>>>>> ---
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>> .c
>>>>>>>>>> +++
>>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.c
>>>>>>>>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
>>>>>>>>>>    EFI_STATUS                           HashStatus;
>>>>>>>>>>    EFI_STATUS                           DbStatus;
>>>>>>>>>>    BOOLEAN                              IsFound;
>>>>>>>>>> +  UINT32                               AlignedLength;
>>>>>>>>>> +  UINT32                               Result;
>>>>>>>>>> +  EFI_STATUS                           AddStatus;
>>>>>>>>>> +  BOOLEAN                              IsAuthDataAssigned;
>>>>>>>>>>
>>>>>>>>>>    SignatureList     = NULL;
>>>>>>>>>>    SignatureListSize = 0;
>>>>>>>>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
>>>>>>>>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
>>>>>>>>>>    IsVerified        = FALSE;
>>>>>>>>>>    IsFound           = FALSE;
>>>>>>>>>> +  Result            = 0;
>>>>>>>>>>
>>>>>>>>>>    //
>>>>>>>>>>    // Check the image type and get policy setting.
>>>>>>>>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
>>>>>>>>>>    // The first certificate starts at offset
>>>>>>>>>> (SecDataDir->VirtualAddress) from
>>>>>> the
>>>>>>>> start of the file.
>>>>>>>>>>    //
>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>> (WinCertificate-
>>>>>>>>> dwLength))) {
>>>>>>>>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet <
>>>>>>>>>> + (SecDataDir-
>>>>>>>>> VirtualAddress + SecDataDir->Size));) {
>>>>>>>>>> +    IsAuthDataAssigned = FALSE;
>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE
>>>>>> (WinCertificate-
>>>>>>>>> dwLength);
>>>>>>>>>
>>>>>>>>> I disagree with this patch.
>>>>>>>>>
>>>>>>>>> The primary reason for my disagreement is that the bug report
>>>>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is
>>>>>>>>> inexact, and so this patch tries to fix the wrong thing.
>>>>>>>>>
>>>>>>>>> With edk2 master at commit 65904cdbb33c, it is *not* possible to
>>>>>>>>> overflow the OffSet variable to zero with "WinCertificate->dwLength"
>>>>>>>>> *purely*, and cause an endless loop. Note that we have (at commit
>>>>>>>>> 65904cdbb33c):
>>>>>>>>>
>>>>>>>>>   for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>> (WinCertificate-
>>>>>>>>> dwLength))) {
>>>>>>>>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>> <= sizeof
>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>>>> WinCertificate-
>>>>>>>>> dwLength) {
>>>>>>>>>       break;
>>>>>>>>>     }
>>>>>>>>>
>>>>>>>>> The last sub-condition checks whether the Security Data Directory
>>>>>>>>> has enough room left for "WinCertificate->dwLength". If not, then
>>>>>>>>> we break out of the loop.
>>>>>>>>>
>>>>>>>>> If we *do* have enough room, that is:
>>>>>>>>>
>>>>>>>>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >=
>>>>>> WinCertificate-
>>>>>>>>> dwLength
>>>>>>>>>
>>>>>>>>> then we have (by adding OffSet to both sides):
>>>>>>>>>
>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet +
>>>>>>>>> WinCertificate- dwLength
>>>>>>>>>
>>>>>>>>> The left hand side is a known-good UINT32, and so incrementing
>>>>>>>>> OffSet (a
>>>>>>>>> UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32)
>>>>>>>>> does not cause an overflow.
>>>>>>>>>
>>>>>>>>> Instead, the problem is with the alignment. The "if" statement
>>>>>>>>> checks whether we have enough room for "dwLength", but then
>>>>>>>>> "OffSet" is advanced by "dwLength" *aligned up* to the next
>>>>>>>>> multiple of 8. And that may indeed cause various overflows.
>>>>>>>>>
>>>>>>>>> Now, the main problem with the present patch is that it does not
>>>>>>>>> fix one of those overflows. Namely, consider that "dwLength" is
>>>>>>>>> very close to
>>>>>>>>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then aligning
>>>>>>>>> it up to the next multiple of 8 will yield 0. In other words,
>> "AlignedLength"
>>>>>>>>> will be zero.
>>>>>>>>>
>>>>>>>>> And when that happens, there's going to be an infinite loop just
>>>>>>>>> the
>>>>>>>>> same: "OffSet" will not be zero, but it will be *stuck*. The
>>>>>>>>> SafeUint32Add() call at the bottom will succeed, but it will not
>>>>>>>>> change the value of "OffSet".
>>>>>>>>>
>>>>>>>>> More at the bottom.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>>> <= sizeof
>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>>> <
>>>>>>>> WinCertificate->dwLength) {
>>>>>>>>>>        break;
>>>>>>>>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
>>>>>>>>>>        }
>>>>>>>>>>        AuthData   = PkcsCertData->CertData;
>>>>>>>>>>        AuthDataSize = PkcsCertData->Hdr.dwLength -
>>>>>>>>>> sizeof(PkcsCertData-
>>>>>>> Hdr);
>>>>>>>>>> +      IsAuthDataAssigned = TRUE;
>>>>>>>>>> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>>>>>      } else if (WinCertificate->wCertificateType ==
>>>>>> WIN_CERT_TYPE_EFI_GUID)
>>>>>>>> {
>>>>>>>>>>        //
>>>>>>>>>>        // The certificate is formatted as
>>>>>>>>>> WIN_CERTIFICATE_UEFI_GUID which
>>>>>> is
>>>>>>>> described in UEFI Spec.
>>>>>>>>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
>>>>>>>>>>        if (WinCertUefiGuid->Hdr.dwLength <=
>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
>>>>>>>>>>          break;
>>>>>>>>>>        }
>>>>>>>>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType,
>> &gEfiCertPkcs7Guid))
>>>>>> {
>>>>>>>>>> -        continue;
>>>>>>>>>> +      if (CompareGuid (&WinCertUefiGuid->CertType,
>>>>>>>>>> + &gEfiCertPkcs7Guid))
>>>>>> {
>>>>>>>>>> +        AuthData = WinCertUefiGuid->CertData;
>>>>>>>>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>>>>> +        IsAuthDataAssigned = TRUE;
>>>>>>>>>> +        HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>>>>>        }
>>>>>>>>>> -      AuthData = WinCertUefiGuid->CertData;
>>>>>>>>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>>>>>      } else {
>>>>>>>>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
>>>>>>>>>>          break;
>>>>>>>>>>        }
>>>>>>>>>> -      continue;
>>>>>>>>>>      }
>>>>>>>>>>
>>>>>>>>>> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>>>>> -    if (EFI_ERROR (HashStatus)) {
>>>>>>>>>> -      continue;
>>>>>>>>>> -    }
>>>>>>>>>> -
>>>>>>>>>> -    //
>>>>>>>>>> -    // Check the digital signature against the revoked certificate in
>>>>>> forbidden
>>>>>>>> database (dbx).
>>>>>>>>>> -    //
>>>>>>>>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>>>>>> -      IsVerified = FALSE;
>>>>>>>>>> -      break;
>>>>>>>>>> -    }
>>>>>>>>>> -
>>>>>>>>>> -    //
>>>>>>>>>> -    // Check the digital signature against the valid certificate in
>> allowed
>>>>>>>> database (db).
>>>>>>>>>> -    //
>>>>>>>>>> -    if (!IsVerified) {
>>>>>>>>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>>>>>> -        IsVerified = TRUE;
>>>>>>>>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
>>>>>>>>>> +      //
>>>>>>>>>> +      // Check the digital signature against the revoked
>>>>>>>>>> + certificate in
>>>>>> forbidden
>>>>>>>> database (dbx).
>>>>>>>>>> +      //
>>>>>>>>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>>>>>> +        IsVerified = FALSE;
>>>>>>>>>> +        break;
>>>>>>>>>>        }
>>>>>>>>>> -    }
>>>>>>>>>>
>>>>>>>>>> -    //
>>>>>>>>>> -    // Check the image's hash value.
>>>>>>>>>> -    //
>>>>>>>>>> -    DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>>>>> -                 mImageDigest,
>>>>>>>>>> -                 &mCertType,
>>>>>>>>>> -                 mImageDigestSize,
>>>>>>>>>> -                 &IsFound
>>>>>>>>>> -                 );
>>>>>>>>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>>>>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
>> signed
>>>>>> but %s
>>>>>>>> hash of image is found in DBX.\n", mHashTypeStr));
>>>>>>>>>> -      IsVerified = FALSE;
>>>>>>>>>> -      break;
>>>>>>>>>> -    }
>>>>>>>>>> +      //
>>>>>>>>>> +      // Check the digital signature against the valid
>>>>>>>>>> + certificate in allowed
>>>>>>>> database (db).
>>>>>>>>>> +      //
>>>>>>>>>> +      if (!IsVerified) {
>>>>>>>>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>>>>>> +          IsVerified = TRUE;
>>>>>>>>>> +        }
>>>>>>>>>> +      }
>>>>>>>>>>
>>>>>>>>>> -    if (!IsVerified) {
>>>>>>>>>> +      //
>>>>>>>>>> +      // Check the image's hash value.
>>>>>>>>>> +      //
>>>>>>>>>>        DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>> -                   EFI_IMAGE_SECURITY_DATABASE,
>>>>>>>>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>>>>>                     mImageDigest,
>>>>>>>>>>                     &mCertType,
>>>>>>>>>>                     mImageDigestSize,
>>>>>>>>>>                     &IsFound
>>>>>>>>>>                     );
>>>>>>>>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>>>>>> -        IsVerified = TRUE;
>>>>>>>>>> -      } else {
>>>>>>>>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
>> signed
>>>>>> but
>>>>>>>> signature is not allowed by DB and %s hash of image is not found in
>>>>>> DB/DBX.\n",
>>>>>>>> mHashTypeStr));
>>>>>>>>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>>>>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
>>>>>>>>>> + signed
>>>>>>>> but %s hash of image is found in DBX.\n", mHashTypeStr));
>>>>>>>>>> +        IsVerified = FALSE;
>>>>>>>>>> +        break;
>>>>>>>>>>        }
>>>>>>>>>> +
>>>>>>>>>> +      if (!IsVerified) {
>>>>>>>>>> +        DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>> +                     EFI_IMAGE_SECURITY_DATABASE,
>>>>>>>>>> +                     mImageDigest,
>>>>>>>>>> +                     &mCertType,
>>>>>>>>>> +                     mImageDigestSize,
>>>>>>>>>> +                     &IsFound
>>>>>>>>>> +                     );
>>>>>>>>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>>>>>> +          IsVerified = TRUE;
>>>>>>>>>> +        } else {
>>>>>>>>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
>>>>>>>>>> + signed
>>>>>> but
>>>>>>>> signature is not allowed by DB and %s hash of image is not found in
>>>>>> DB/DBX.\n",
>>>>>>>> mHashTypeStr));
>>>>>>>>>> +        }
>>>>>>>>>> +      }
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
>>>>>>>>>> +    if (EFI_ERROR (AddStatus)) {
>>>>>>>>>> +      break;
>>>>>>>>>>      }
>>>>>>>>>> +    OffSet = Result;
>>>>>>>>>>    }
>>>>>>>>>>
>>>>>>>>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
>>>>>>>>>> {
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> There are other (smaller) reasons why I dislike this patch:
>>>>>>>>>
>>>>>>>>> - The "IsAuthDataAssigned" variable is superfluous; we could use
>>>>>>>>> the existent "AuthData" variable (with a NULL-check and a
>>>>>>>>> NULL-assignment) similarly.
>>>>>>>>>
>>>>>>>>> - The patch complicates / reorganizes the control flow needlessly.
>>>>>>>>> This complication originates from placing the checked "OffSet"
>>>>>>>>> increment at the bottom of the loop, which then requires the
>>>>>>>>> removal of all the "continue" statements. But we don't need to
>>>>>>>>> check-and-increment at the bottom. We can keep the increment
>>>>>>>>> inside the "for" statement, only extend the *existent* room check
>>>>>>>>> (which I've quoted) to take the alignment into account as well. If
>>>>>>>>> there is enough room for the alignment in the security data
>>>>>>>>> directory, then that guarantees there won't be a UINT32 overflow
>> either.
>>>>>>>>>
>>>>>>>>> All in all, I'm proposing the following three patches instead. The
>>>>>>>>> first two patches are preparation, the last patch is the fix.
>>>>>>>>>
>>>>>>>>> Patch#1:
>>>>>>>>>
>>>>>>>>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep 17
>>>>>> 00:00:00
>>>>>>>> 2001
>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
>>>>>>>>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib: extract
>>>>>>>>>> SecDataDirEnd, SecDataDirLeft
>>>>>>>>>>
>>>>>>>>>> The following two quantities:
>>>>>>>>>>
>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
>>>>>>>>>>
>>>>>>>>>> are used multiple times in DxeImageVerificationHandler().
>>>>>>>>>> Introduce helper variables for them: "SecDataDirEnd" and
>> "SecDataDirLeft", respectively.
>>>>>>>>>> This saves us multiple calculations and significantly simplifies the code.
>>>>>>>>>>
>>>>>>>>>> Note that all three summands above have type UINT32, therefore
>>>>>>>>>> the new variables are also of type UINT32.
>>>>>>>>>>
>>>>>>>>>> This patch does not change behavior.
>>>>>>>>>>
>>>>>>>>>> (Note that the code already handles the case when the
>>>>>>>>>>
>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>>>>>
>>>>>>>>>> UINT32 addition overflows -- namely, in that case, the
>>>>>>>>>> certificate loop is never entered, and the corruption check right
>>>>>>>>>> after the loop fires.)
>>>>>>>>>>
>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>> ---
>>>>>>>>>>
>>>>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.c |
>>>>>> 12
>>>>>>>> ++++++++----
>>>>>>>>>>  1 file changed, 8 insertions(+), 4 deletions(-)
>>>>>>>>>>
>>>>>>>>>> diff --git
>>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.c
>>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.c
>>>>>>>>>> index 36b87e16d53d..8761980c88aa 100644
>>>>>>>>>> ---
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>> .c
>>>>>>>>>> +++
>>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.c
>>>>>>>>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
>>>>>>>>>>    UINT8                                *AuthData;
>>>>>>>>>>    UINTN                                AuthDataSize;
>>>>>>>>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
>>>>>>>>>> +  UINT32                               SecDataDirEnd;
>>>>>>>>>> +  UINT32                               SecDataDirLeft;
>>>>>>>>>>    UINT32                               OffSet;
>>>>>>>>>>    CHAR16                               *NameStr;
>>>>>>>>>>    RETURN_STATUS                        PeCoffStatus;
>>>>>>>>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
>>>>>>>>>>    // "Attribute Certificate Table".
>>>>>>>>>>    // The first certificate starts at offset
>>>>>>>>>> (SecDataDir->VirtualAddress) from
>>>>>> the
>>>>>>>> start of the file.
>>>>>>>>>>    //
>>>>>>>>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>> +       OffSet < SecDataDirEnd;
>>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>> (WinCertificate-
>>>>>>>>> dwLength))) {
>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <=
>> sizeof
>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>>>>>> WinCertificate->dwLength) {
>>>>>>>>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>>>>>> +        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>        break;
>>>>>>>>>>      }
>>>>>>>>>>
>>>>>>>>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
>>>>>>>>>>      }
>>>>>>>>>>    }
>>>>>>>>>>
>>>>>>>>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
>>>>>>>>>> {
>>>>>>>>>> +  if (OffSet != SecDataDirEnd) {
>>>>>>>>>>      //
>>>>>>>>>>      // The Size in Certificate Table or the attribute
>>>>>>>>>> certificate table is
>>>>>> corrupted.
>>>>>>>>>>      //
>>>>>>>>>> --
>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Patch#2:
>>>>>>>>>
>>>>>>>>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep 17
>> 00:00:00
>>>>>>>> 2001
>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
>>>>>>>>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib: assign
>>>>>>>>>> WinCertificate after size check
>>>>>>>>>>
>>>>>>>>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) check
>>>>>>>>>> only guards the de-referencing of the "WinCertificate" pointer.
>>>>>>>>>> It does not guard the calculation of hte pointer itself:
>>>>>>>>>>
>>>>>>>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>
>>>>>>>>>> This is wrong; if we don't know for sure that we have enough room
>>>>>>>>>> for a WIN_CERTIFICATE, then even creating such a pointer, not
>>>>>>>>>> just de-referencing it, may invoke undefined behavior.
>>>>>>>>>>
>>>>>>>>>> Move the pointer calculation after the size check.
>>>>>>>>>>
>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>> ---
>>>>>>>>>>
>>>>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.c |
>>>>>> 8
>>>>>>>> +++++---
>>>>>>>>>>  1 file changed, 5 insertions(+), 3 deletions(-)
>>>>>>>>>>
>>>>>>>>>> diff --git
>>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.c
>>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.c
>>>>>>>>>> index 8761980c88aa..461ed7cfb5ac 100644
>>>>>>>>>> ---
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>> .c
>>>>>>>>>> +++
>>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.c
>>>>>>>>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>         OffSet < SecDataDirEnd;
>>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>> (WinCertificate-
>>>>>>>>> dwLength))) {
>>>>>>>>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>>>>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>>>>>> -        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
>>>>>>>>>> +      break;
>>>>>>>>>> +    }
>>>>>>>>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>        break;
>>>>>>>>>>      }
>>>>>>>>>>
>>>>>>>>>> --
>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Patch#3:
>>>>>>>>>
>>>>>>>>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17
>> 00:00:00
>>>>>>>> 2001
>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
>>>>>>>>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch
>>>>>> alignment
>>>>>>>>>>  overflow (CVE-2019-14562)
>>>>>>>>>>
>>>>>>>>>> The DxeImageVerificationHandler() function currently checks
>>>>>>>>>> whether "SecDataDir" has enough room for
>>>>>>>>>> "WinCertificate->dwLength". However,
>>>>>>>> for
>>>>>>>>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to the
>>>>>>>>>> next multiple of 8. If "WinCertificate->dwLength" is large
>>>>>>>>>> enough, the alignment will return 0, and "OffSet" will be stuck at the
>> same value.
>>>>>>>>>>
>>>>>>>>>> Check whether "SecDataDir" has room left for both
>>>>>>>>>> "WinCertificate->dwLength" and the alignment.
>>>>>>>>>>
>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>> ---
>>>>>>>>>>
>>>>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.c |
>>>>>> 4
>>>>>>>> +++-
>>>>>>>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>>>>>>>>
>>>>>>>>>> diff --git
>>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.c
>>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.c
>>>>>>>>>> index 461ed7cfb5ac..e38eb981b7a0 100644
>>>>>>>>>> ---
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>> .c
>>>>>>>>>> +++
>>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>> ib.c
>>>>>>>>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
>>>>>>>>>>        break;
>>>>>>>>>>      }
>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
>>>>>>>>>> +        (SecDataDirLeft - WinCertificate->dwLength <
>>>>>>>>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
>>>>>>>>>>        break;
>>>>>>>>>>      }
>>>>>>>>>>
>>>>>>>>>> --
>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> If Wenyi and the reviewers are OK with these patches, I can submit
>>>>>>>>> them as a standalone patch series.
>>>>>>>>>
>>>>>>>>> Note that I do not have any reproducer for the issue; the best
>>>>>>>>> testing that I could offer would be some light-weight Secure Boot
>>>>>>>>> regression tests.
>>>>>>>>>
>>>>>>>>> Thanks
>>>>>>>>> Laszlo
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> .
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>>
>>> .
>>>
>>
>>
>> 
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-28  6:18                       ` wenyi,xie
@ 2020-08-28  6:43                         ` Yao, Jiewen
  2020-08-31 11:23                           ` wenyi,xie
  0 siblings, 1 reply; 32+ messages in thread
From: Yao, Jiewen @ 2020-08-28  6:43 UTC (permalink / raw)
  To: devel@edk2.groups.io, xiewenyi2@huawei.com, Laszlo Ersek,
	Wang, Jian J
  Cc: songdongkuang@huawei.com, Mathews, John

Apology that I did not say clearly.
I mean you should not modify any code to perform an attack.

I am not asking you to exploit the system. Attack here just means: to cause system hang or buffer overflow. That is enough.
But you cannot modify code to remove any existing checker.

Thank you
Yao Jiewen

> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of wenyi,xie
> via groups.io
> Sent: Friday, August 28, 2020 2:18 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Laszlo Ersek
> <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>
> Cc: songdongkuang@huawei.com; Mathews, John <john.mathews@intel.com>
> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> 
> Hi,Jiewen,
> 
> I don't really get the meaning "create a case that bypass all checks in PeCoffLib",
> do you mean I need to create a PE file that can bypass all check in PeCoffLib
> without modify any
> code and then cause the problem in DxeImageVerificationLib, or just modify
> some code in PeCoffLib to bypass check instead of removing the calling of
> PeCoffLoaderGetImageInfo. Would
> you mind explaining a little more specifically? As far as I tried, it's really hard to
> reproduce the issue without touching any code.
> 
> Thanks
> Wenyi
> 
> On 2020/8/28 11:50, Yao, Jiewen wrote:
> > HI Wenyi
> > Thank you very much to take time to reproduce.
> >
> > I am particular interested in below:
> > 	"As PE file is modified, function PeCoffLoaderGetImageInfo will return
> error, so I have to remove it so that for loop can be tested in
> DxeImageVerificationHandler."
> >
> > By design, the PeCoffLib should catch illegal PE/COFF image and return error.
> (even it cannot catch all, it should catch most ones).
> > Other PE/COFF parser may rely on the checker in PeCoffLib and *no need* to
> duplicate all checkers.
> > As such, DxeImageVerificationLib (and other PeCoff consumer) just need
> checks what has passed the check in PeCoffLib.
> >
> > I don’t think you should remove the checker. If people can remove the checker,
> I am sure the rest code will be vulnerable, according to the current design.
> > Could you please to create a case that bypass all checks in PeCoffLib, then run
> into DxeImageVerificationLib and cause the problem? That would be more
> valuable for us.
> >
> > Thank you
> > Yao Jiewen
> >
> >> -----Original Message-----
> >> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
> wenyi,xie
> >> via groups.io
> >> Sent: Friday, August 28, 2020 11:18 AM
> >> To: Laszlo Ersek <lersek@redhat.com>; Wang, Jian J
> <jian.j.wang@intel.com>;
> >> devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>
> >> Cc: songdongkuang@huawei.com; Mathews, John
> <john.mathews@intel.com>
> >> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> >>
> >> Hi,Laszlo and everyone,
> >>
> >> These days I tried to reproduce the issue,and made some progress. I think
> >> there are two way to cause overflow from a mathematical point of view,
> >> 1.As Laszlo analysed before, if WinCertificate->dwLength is large enough,
> close
> >> to MAX_UINT32, then (WinCertificate->dwLength + ALIGN_SIZE
> (WinCertificate-
> >>> dwLength)) will cause overflow.
> >> 2.(WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength)) is
> good,
> >> OffSet is good, but OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >> (WinCertificate->dwLength)) cause overflow.
> >>
> >> Here I choose the second way to reproduce the issue, I choose a PE file and
> sign
> >> it with my own db certificate. Then I use binary edit tool to modify the PE file
> like
> >> below,
> >>
> >> 1.change SecDataDir->Size from 0x5F8 to 0xFFFF1FFF
> >> 2.change WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFE
> >> SecDataDir->VirtualAddress in my PE is 0xe000 and no need to change.
> >>
> >> As PE file is modified, function PeCoffLoaderGetImageInfo will return error,
> so I
> >> have to remove it so that for loop can be tested in
> DxeImageVerificationHandler.
> >> The log is as below,
> >>
> >> SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFF.
> >> (First Loop)
> >> OffSet=0xE000.
> >> WinCertificate->dwLength=0xFFFF1FFE, ALIGN_SIZE (WinCertificate-
> >>> dwLength)=0x2.
> >> (Second Loop)
> >> OffSet=0x0.
> >> WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate-
> >>> dwLength)=0x3.
> >> (Third Loop)
> >> OffSet=0x5A50.
> >> WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate-
> >>> dwLength)=0x4.
> >> (Forth Loop)
> >> OffSet=0xEA78.
> >> WinCertificate->dwLength=0xAFAFAFAF, ALIGN_SIZE (WinCertificate-
> >>> dwLength)=0x1.
> >> (Fifth Loop)
> >> OffSet=0xAFB09A28.
> >>
> >> As I modify SecDataDir->Size and WinCertificate->dwLength, so in first loop
> the
> >> condition check whether the Security Data Directory has enough room left
> for
> >> "WinCertificate->dwLength" is ok.
> >>
> >> if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
> >> (WIN_CERTIFICATE) ||
> >>     (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
> >>> dwLength) {
> >>   break;
> >> }
> >>
> >> In the beginning of second loop, WinCertificate->dwLength + ALIGN_SIZE
> >> (WinCertificate->dwLength) is 0xFFFF2000, offset is 0xE000
> >>
> >> OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >dwLength))
> >>
> >> Offset now is 0 and overflow happens. So even if my PE only have one
> signature,
> >> the for loop is still going untill exception happens.
> >>
> >>
> >> I can't reproduce the issue using the first way, because if WinCertificate-
> >>> dwLength is close to MAX_UINT32, it means SecDataDir->Size should also
> close
> >> to MAX_UINT32, or the condition check
> >> "(SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
> >>> dwLength" will break. But if SecDataDir->Size is very large, SecDataDir-
> >>> VirtualAddress have to be smaller than 8 bytes,
> >> because SecDataDir->VirtualAddress + SecDataDir->Size < MAX_UINT32.
> >> SecDataDir->VirtualAddress is the beginning address of the signature, and
> before
> >> SecDataDir->VirtualAddress is the content of origin PE file, I think it's
> impossible
> >> that the size of PE file is only 8 bytes.
> >>
> >> As I changed the one line code in DxeImageVerificationHandler to reproduce
> the
> >> issue, I'm not sure whether it's ok.
> >>
> >> Thanks
> >> Wenyi
> >>
> >> On 2020/8/19 17:26, Laszlo Ersek wrote:
> >>> On 08/18/20 17:18, Mathews, John wrote:
> >>>> I dug up the original report details.  This was noted as a concern during a
> >> source code inspection.  There was no demonstration of how it might be
> >> triggered.
> >>>>
> >>>> " There is an integer overflow vulnerability in the
> >> DxeImageVerificationHandler function when
> >>>> parsing the PE files attribute certificate table. In cases where
> WinCertificate-
> >>> dwLength is
> >>>> sufficiently large, it's possible to overflow Offset back to 0 causing an
> endless
> >> loop."
> >>>>
> >>>> The recommendation was to add stricter checking of "Offset" and the
> >> embedded length fields of certificate data
> >>>> before using them.
> >>>
> >>> Thanks for checking!
> >>>
> >>> Laszlo
> >>>
> >>>>
> >>>>
> >>>>
> >>>> -----Original Message-----
> >>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>> Sent: Tuesday, August 18, 2020 1:59 AM
> >>>> To: Wang, Jian J <jian.j.wang@intel.com>; devel@edk2.groups.io; Yao,
> >> Jiewen <jiewen.yao@intel.com>; xiewenyi2@huawei.com
> >>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com; Mathews,
> >> John <john.mathews@intel.com>
> >>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> >>>>
> >>>> On 08/18/20 04:10, Wang, Jian J wrote:
> >>>>> Laszlo,
> >>>>>
> >>>>> My apologies for the slow response. I'm not the original reporter but
> >>>>> just the BZ submitter. And I didn't do deep analysis to this issue.
> >>>>> The issues was reported from one internal team. Add John in loop to see
> if
> >> he knows more about it or not.
> >>>>>
> >>>>> My superficial understanding on such issue is that, if there's
> >>>>> "potential" issue in theory and hard to reproduce, it's still worthy
> >>>>> of using an alternative way to replace the original implementation
> >>>>> with no "potential" issue at all. Maybe we don't have to prove old way is
> >> something wrong but must prove that the new way is really safe.
> >>>>
> >>>> I agree, thanks.
> >>>>
> >>>> It would be nice to hear more from the internal team about the originally
> >> reported (even if hard-to-trigger) issue.
> >>>>
> >>>> Thanks!
> >>>> Laszlo
> >>>>
> >>>>>
> >>>>> Regards,
> >>>>> Jian
> >>>>>
> >>>>>> -----Original Message-----
> >>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
> Laszlo
> >>>>>> Ersek
> >>>>>> Sent: Tuesday, August 18, 2020 12:53 AM
> >>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
> >>>>>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
> >>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
> >>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> >>>>>>
> >>>>>> Hi Jiewen,
> >>>>>>
> >>>>>> On 08/14/20 10:53, Yao, Jiewen wrote:
> >>>>>>>> To Jiewen,
> >>>>>>>> Sorry, I don't have environment to reproduce the issue.
> >>>>>>>
> >>>>>>> Please help me understand, if you don’t have environment to
> >>>>>>> reproduce the
> >>>>>> issue, how do you guarantee that your patch does fix the problem and
> >>>>>> we don’t have any other vulnerabilities?
> >>>>>>
> >>>>>> The original bug report in
> >>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is seriously
> >>>>>> lacking. It does not go into detail about the alleged integer overflow.
> >>>>>> It does not quote the code, does not explain the control flow, does
> >>>>>> not identify the exact edk2 commit at which the vulnerability exists.
> >>>>>>
> >>>>>> The bug report also does not offer a reproducer.
> >>>>>>
> >>>>>> Additionally, the exact statement that the bug report does make,
> >>>>>> namely
> >>>>>>
> >>>>>>   it's possible to overflow Offset back to 0 causing an endless loop
> >>>>>>
> >>>>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can
> >>>>>> be overflowed to zero, but the *addend* that is added to OffSet can
> >>>>>> be overflowed to zero. Therefore the infinite loop will arise because
> >>>>>> OffSet remains stuck at its present value, and not because OffSet
> >>>>>> will be re-set to zero.
> >>>>>>
> >>>>>> For the reasons, we can only speculate as to what the actual problem
> >>>>>> is, unless Jian decides to join the discussion and clarifies what he
> >>>>>> had in mind originally.
> >>>>>>
> >>>>>> My understanding (or even "reconstruction") of the vulnerability is
> >>>>>> described above, and in the patches that I proposed.
> >>>>>>
> >>>>>> We can write a patch based on code analysis. It's possible to
> >>>>>> identify integer overflows based on code analysis, and it's possible
> >>>>>> to verify the correctness of fixes by code review. Obviously testing
> >>>>>> is always good, but many times, constructing reproducers for such
> >>>>>> issues that were found by code review, is difficult and time
> >>>>>> consuming. We can say that we don't fix vulnerabilities without
> >>>>>> reproducers, or we can say that we make an effort to fix them even if
> >>>>>> all we have is code analysis (and not a reproducer).
> >>>>>>
> >>>>>> So the above paragraph concerns "correctness". Regarding
> >>>>>> "completeness", I guarantee you that this patch does not fix *all*
> >>>>>> problems related to PE parsing. (See the other BZ tickets.) It does
> >>>>>> fix *one* issue with PE parsing. We can say that we try to fix such
> >>>>>> issues gradually (give different CVE numbers to different issues, and
> >>>>>> address them one at a time), or we can say that we rewrite PE parsing
> >> from the ground up.
> >>>>>> (BTW: I have seriously attempted that in the past, and I gave up,
> >>>>>> because the PE format is FUBAR.)
> >>>>>>
> >>>>>> In summary:
> >>>>>>
> >>>>>> - the problem statement is unclear,
> >>>>>>
> >>>>>> - it seems like there is indeed an integer overflow problem in the
> >>>>>> SecDataDir parsing loop, but it's uncertain whether the bug reporter
> >>>>>> had exactly that in mind
> >>>>>>
> >>>>>> - PE parsing is guaranteed to have other vulnerabilities elsewhere in
> >>>>>> edk2, but I'm currently unaware of other such issues in
> >>>>>> DxeImageVerificationLib specifically
> >>>>>>
> >>>>>> - even if there are other such problems (in DxeImageVerificationLib
> >>>>>> or elswehere), fixing this bug that we know about is likely
> >>>>>> worthwhile
> >>>>>>
> >>>>>> - for many such bugs, constructing a reproducer is difficult and time
> >>>>>> consuming; code analysis, and *regression-testing* are frequently the
> >>>>>> only tools we have. That doesn't mean we should ignore this class of
> bugs.
> >>>>>>
> >>>>>> (Fixing integer overflows retro-actively is more difficult than
> >>>>>> writing overflow-free code in the first place, but that ship has
> >>>>>> sailed; so we can only fight these bugs incrementally now, unless we
> >>>>>> can rewrite PE parsing with a new data structure from the ground up.
> >>>>>> Again I tried that and gave up, because the spec is not public, and
> >>>>>> what I did manage to learn about PE, showed that it was insanely
> >>>>>> over-engineered. I'm not saying that other binary / executable
> >>>>>> formats are better, of course.)
> >>>>>>
> >>>>>> Please check out my patches (inlined elsewhere in this thread), and
> >>>>>> comment whether you'd like me to post them to the list as a
> >>>>>> standalone series.
> >>>>>>
> >>>>>> Jian: it wouldn't hurt if you commented as well.
> >>>>>>
> >>>>>> Thanks
> >>>>>> Laszlo
> >>>>>>
> >>>>>>>> -----Original Message-----
> >>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
> >>>>>> wenyi,xie
> >>>>>>>> via groups.io
> >>>>>>>> Sent: Friday, August 14, 2020 3:54 PM
> >>>>>>>> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io; Yao,
> >>>>>>>> Jiewen <jiewen.yao@intel.com>; Wang, Jian J
> <jian.j.wang@intel.com>
> >>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
> >>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> >>>>>>>>
> >>>>>>>> To Laszlo,
> >>>>>>>> Thank you for your detailed description, I agree with what you
> >>>>>>>> analyzed and
> >>>>>> I'm
> >>>>>>>> OK with your patches, it's
> >>>>>>>> correct and much simpler.
> >>>>>>>>
> >>>>>>>> To Jiewen,
> >>>>>>>> Sorry, I don't have environment to reproduce the issue.
> >>>>>>>>
> >>>>>>>> Thanks
> >>>>>>>> Wenyi
> >>>>>>>>
> >>>>>>>> On 2020/8/14 2:50, Laszlo Ersek wrote:
> >>>>>>>>> On 08/13/20 13:55, Wenyi Xie wrote:
> >>>>>>>>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
> >>>>>>>>>>
> >>>>>>>>>> There is an integer overflow vulnerability in
> >>>>>>>>>> DxeImageVerificationHandler function when parsing the PE files
> >>>>>>>>>> attribute certificate table. In cases where
> >>>>>>>>>> WinCertificate->dwLength is sufficiently large, it's possible to
> >> overflow Offset back to 0 causing an endless loop.
> >>>>>>>>>>
> >>>>>>>>>> Check offset inbetween VirtualAddress and VirtualAddress + Size.
> >>>>>>>>>> Using SafeintLib to do offset addition with result check.
> >>>>>>>>>>
> >>>>>>>>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
> >>>>>>>>>> Cc: Jian J Wang <jian.j.wang@intel.com>
> >>>>>>>>>> Cc: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
> >>>>>>>>>> ---
> >>>>>>>>>>
> >>>>>>>>>>
> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.inf
> >>>>>> |
> >>>>>>>> 1 +
> >>>>>>>>>>
> >>>>>>>>>>
> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.h
> >>>>>> |
> >>>>>>>> 1 +
> >>>>>>>>>>
> >>>>>>>>>>
> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.c
> >>>>>> |
> >>>>>>>> 111 +++++++++++---------
> >>>>>>>>>>  3 files changed, 63 insertions(+), 50 deletions(-)
> >>>>>>>>>>
> >>>>>>>>>> diff --git
> >>>>>>>>
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.inf
> >>>>>>>>
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.inf
> >>>>>>>>>> index 1e1a639857e0..a7ac4830b3d4 100644
> >>>>>>>>>> ---
> >>>>>>>>
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.inf
> >>>>>>>>>> +++
> >>>>>>>>
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.inf
> >>>>>>>>>> @@ -53,6 +53,7 @@ [LibraryClasses]
> >>>>>>>>>>    SecurityManagementLib
> >>>>>>>>>>    PeCoffLib
> >>>>>>>>>>    TpmMeasurementLib
> >>>>>>>>>> +  SafeIntLib
> >>>>>>>>>>
> >>>>>>>>>>  [Protocols]
> >>>>>>>>>>    gEfiFirmwareVolume2ProtocolGuid       ##
> SOMETIMES_CONSUMES
> >>>>>>>>>> diff --git
> >>>>>>>>
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.h
> >>>>>>>>
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.h
> >>>>>>>>>> index 17955ff9774c..060273917d5d 100644
> >>>>>>>>>> ---
> >>>>>>>>
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.h
> >>>>>>>>>> +++
> >>>>>>>>
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.h
> >>>>>>>>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
> >>>>>>>>>> #include <Library/DevicePathLib.h>  #include
> >>>>>>>>>> <Library/SecurityManagementLib.h>  #include <Library/PeCoffLib.h>
> >>>>>>>>>> +#include <Library/SafeIntLib.h>
> >>>>>>>>>>  #include <Protocol/FirmwareVolume2.h>  #include
> >>>>>>>>>> <Protocol/DevicePath.h>  #include <Protocol/BlockIo.h> diff --git
> >>>>>>>>
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.c
> >>>>>>>>
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.c
> >>>>>>>>>> index 36b87e16d53d..dbc03e28c05b 100644
> >>>>>>>>>> ---
> >>>>>>
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
> >>>>>> .c
> >>>>>>>>>> +++
> >>>>>>>>
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.c
> >>>>>>>>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
> >>>>>>>>>>    EFI_STATUS                           HashStatus;
> >>>>>>>>>>    EFI_STATUS                           DbStatus;
> >>>>>>>>>>    BOOLEAN                              IsFound;
> >>>>>>>>>> +  UINT32                               AlignedLength;
> >>>>>>>>>> +  UINT32                               Result;
> >>>>>>>>>> +  EFI_STATUS                           AddStatus;
> >>>>>>>>>> +  BOOLEAN                              IsAuthDataAssigned;
> >>>>>>>>>>
> >>>>>>>>>>    SignatureList     = NULL;
> >>>>>>>>>>    SignatureListSize = 0;
> >>>>>>>>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
> >>>>>>>>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
> >>>>>>>>>>    IsVerified        = FALSE;
> >>>>>>>>>>    IsFound           = FALSE;
> >>>>>>>>>> +  Result            = 0;
> >>>>>>>>>>
> >>>>>>>>>>    //
> >>>>>>>>>>    // Check the image type and get policy setting.
> >>>>>>>>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
> >>>>>>>>>>    // The first certificate starts at offset
> >>>>>>>>>> (SecDataDir->VirtualAddress) from
> >>>>>> the
> >>>>>>>> start of the file.
> >>>>>>>>>>    //
> >>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>>>>>>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >> (WinCertificate-
> >>>>>>>>> dwLength))) {
> >>>>>>>>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet <
> >>>>>>>>>> + (SecDataDir-
> >>>>>>>>> VirtualAddress + SecDataDir->Size));) {
> >>>>>>>>>> +    IsAuthDataAssigned = FALSE;
> >>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE
> >>>>>> (WinCertificate-
> >>>>>>>>> dwLength);
> >>>>>>>>>
> >>>>>>>>> I disagree with this patch.
> >>>>>>>>>
> >>>>>>>>> The primary reason for my disagreement is that the bug report
> >>>>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is
> >>>>>>>>> inexact, and so this patch tries to fix the wrong thing.
> >>>>>>>>>
> >>>>>>>>> With edk2 master at commit 65904cdbb33c, it is *not* possible to
> >>>>>>>>> overflow the OffSet variable to zero with "WinCertificate-
> >dwLength"
> >>>>>>>>> *purely*, and cause an endless loop. Note that we have (at commit
> >>>>>>>>> 65904cdbb33c):
> >>>>>>>>>
> >>>>>>>>>   for (OffSet = SecDataDir->VirtualAddress;
> >>>>>>>>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>>>>>>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >>>>>>>>> (WinCertificate-
> >>>>>>>>> dwLength))) {
> >>>>>>>>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
> >>>>>>>>> <= sizeof
> >>>>>>>> (WIN_CERTIFICATE) ||
> >>>>>>>>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> >>>>>> WinCertificate-
> >>>>>>>>> dwLength) {
> >>>>>>>>>       break;
> >>>>>>>>>     }
> >>>>>>>>>
> >>>>>>>>> The last sub-condition checks whether the Security Data Directory
> >>>>>>>>> has enough room left for "WinCertificate->dwLength". If not, then
> >>>>>>>>> we break out of the loop.
> >>>>>>>>>
> >>>>>>>>> If we *do* have enough room, that is:
> >>>>>>>>>
> >>>>>>>>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >=
> >>>>>> WinCertificate-
> >>>>>>>>> dwLength
> >>>>>>>>>
> >>>>>>>>> then we have (by adding OffSet to both sides):
> >>>>>>>>>
> >>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet +
> >>>>>>>>> WinCertificate- dwLength
> >>>>>>>>>
> >>>>>>>>> The left hand side is a known-good UINT32, and so incrementing
> >>>>>>>>> OffSet (a
> >>>>>>>>> UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32)
> >>>>>>>>> does not cause an overflow.
> >>>>>>>>>
> >>>>>>>>> Instead, the problem is with the alignment. The "if" statement
> >>>>>>>>> checks whether we have enough room for "dwLength", but then
> >>>>>>>>> "OffSet" is advanced by "dwLength" *aligned up* to the next
> >>>>>>>>> multiple of 8. And that may indeed cause various overflows.
> >>>>>>>>>
> >>>>>>>>> Now, the main problem with the present patch is that it does not
> >>>>>>>>> fix one of those overflows. Namely, consider that "dwLength" is
> >>>>>>>>> very close to
> >>>>>>>>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then aligning
> >>>>>>>>> it up to the next multiple of 8 will yield 0. In other words,
> >> "AlignedLength"
> >>>>>>>>> will be zero.
> >>>>>>>>>
> >>>>>>>>> And when that happens, there's going to be an infinite loop just
> >>>>>>>>> the
> >>>>>>>>> same: "OffSet" will not be zero, but it will be *stuck*. The
> >>>>>>>>> SafeUint32Add() call at the bottom will succeed, but it will not
> >>>>>>>>> change the value of "OffSet".
> >>>>>>>>>
> >>>>>>>>> More at the bottom.
> >>>>>>>>>
> >>>>>>>>>
> >>>>>>>>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
> >>>>>>>>>> <= sizeof
> >>>>>>>> (WIN_CERTIFICATE) ||
> >>>>>>>>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
> >>>>>>>>>> <
> >>>>>>>> WinCertificate->dwLength) {
> >>>>>>>>>>        break;
> >>>>>>>>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
> >>>>>>>>>>        }
> >>>>>>>>>>        AuthData   = PkcsCertData->CertData;
> >>>>>>>>>>        AuthDataSize = PkcsCertData->Hdr.dwLength -
> >>>>>>>>>> sizeof(PkcsCertData-
> >>>>>>> Hdr);
> >>>>>>>>>> +      IsAuthDataAssigned = TRUE;
> >>>>>>>>>> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> >>>>>>>>>>      } else if (WinCertificate->wCertificateType ==
> >>>>>> WIN_CERT_TYPE_EFI_GUID)
> >>>>>>>> {
> >>>>>>>>>>        //
> >>>>>>>>>>        // The certificate is formatted as
> >>>>>>>>>> WIN_CERTIFICATE_UEFI_GUID which
> >>>>>> is
> >>>>>>>> described in UEFI Spec.
> >>>>>>>>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
> >>>>>>>>>>        if (WinCertUefiGuid->Hdr.dwLength <=
> >>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
> >>>>>>>>>>          break;
> >>>>>>>>>>        }
> >>>>>>>>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType,
> >> &gEfiCertPkcs7Guid))
> >>>>>> {
> >>>>>>>>>> -        continue;
> >>>>>>>>>> +      if (CompareGuid (&WinCertUefiGuid->CertType,
> >>>>>>>>>> + &gEfiCertPkcs7Guid))
> >>>>>> {
> >>>>>>>>>> +        AuthData = WinCertUefiGuid->CertData;
> >>>>>>>>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
> >>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
> >>>>>>>>>> +        IsAuthDataAssigned = TRUE;
> >>>>>>>>>> +        HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> >>>>>>>>>>        }
> >>>>>>>>>> -      AuthData = WinCertUefiGuid->CertData;
> >>>>>>>>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
> >>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
> >>>>>>>>>>      } else {
> >>>>>>>>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
> >>>>>>>>>>          break;
> >>>>>>>>>>        }
> >>>>>>>>>> -      continue;
> >>>>>>>>>>      }
> >>>>>>>>>>
> >>>>>>>>>> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> >>>>>>>>>> -    if (EFI_ERROR (HashStatus)) {
> >>>>>>>>>> -      continue;
> >>>>>>>>>> -    }
> >>>>>>>>>> -
> >>>>>>>>>> -    //
> >>>>>>>>>> -    // Check the digital signature against the revoked certificate in
> >>>>>> forbidden
> >>>>>>>> database (dbx).
> >>>>>>>>>> -    //
> >>>>>>>>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> >>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> >>>>>>>>>> -      IsVerified = FALSE;
> >>>>>>>>>> -      break;
> >>>>>>>>>> -    }
> >>>>>>>>>> -
> >>>>>>>>>> -    //
> >>>>>>>>>> -    // Check the digital signature against the valid certificate in
> >> allowed
> >>>>>>>> database (db).
> >>>>>>>>>> -    //
> >>>>>>>>>> -    if (!IsVerified) {
> >>>>>>>>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
> >>>>>>>>>> -        IsVerified = TRUE;
> >>>>>>>>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
> >>>>>>>>>> +      //
> >>>>>>>>>> +      // Check the digital signature against the revoked
> >>>>>>>>>> + certificate in
> >>>>>> forbidden
> >>>>>>>> database (dbx).
> >>>>>>>>>> +      //
> >>>>>>>>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> >>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> >>>>>>>>>> +        IsVerified = FALSE;
> >>>>>>>>>> +        break;
> >>>>>>>>>>        }
> >>>>>>>>>> -    }
> >>>>>>>>>>
> >>>>>>>>>> -    //
> >>>>>>>>>> -    // Check the image's hash value.
> >>>>>>>>>> -    //
> >>>>>>>>>> -    DbStatus = IsSignatureFoundInDatabase (
> >>>>>>>>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
> >>>>>>>>>> -                 mImageDigest,
> >>>>>>>>>> -                 &mCertType,
> >>>>>>>>>> -                 mImageDigestSize,
> >>>>>>>>>> -                 &IsFound
> >>>>>>>>>> -                 );
> >>>>>>>>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
> >>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> >>>>>>>>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
> >> signed
> >>>>>> but %s
> >>>>>>>> hash of image is found in DBX.\n", mHashTypeStr));
> >>>>>>>>>> -      IsVerified = FALSE;
> >>>>>>>>>> -      break;
> >>>>>>>>>> -    }
> >>>>>>>>>> +      //
> >>>>>>>>>> +      // Check the digital signature against the valid
> >>>>>>>>>> + certificate in allowed
> >>>>>>>> database (db).
> >>>>>>>>>> +      //
> >>>>>>>>>> +      if (!IsVerified) {
> >>>>>>>>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
> >>>>>>>>>> +          IsVerified = TRUE;
> >>>>>>>>>> +        }
> >>>>>>>>>> +      }
> >>>>>>>>>>
> >>>>>>>>>> -    if (!IsVerified) {
> >>>>>>>>>> +      //
> >>>>>>>>>> +      // Check the image's hash value.
> >>>>>>>>>> +      //
> >>>>>>>>>>        DbStatus = IsSignatureFoundInDatabase (
> >>>>>>>>>> -                   EFI_IMAGE_SECURITY_DATABASE,
> >>>>>>>>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
> >>>>>>>>>>                     mImageDigest,
> >>>>>>>>>>                     &mCertType,
> >>>>>>>>>>                     mImageDigestSize,
> >>>>>>>>>>                     &IsFound
> >>>>>>>>>>                     );
> >>>>>>>>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
> >>>>>>>>>> -        IsVerified = TRUE;
> >>>>>>>>>> -      } else {
> >>>>>>>>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
> >> signed
> >>>>>> but
> >>>>>>>> signature is not allowed by DB and %s hash of image is not found in
> >>>>>> DB/DBX.\n",
> >>>>>>>> mHashTypeStr));
> >>>>>>>>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
> >>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> >>>>>>>>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
> >>>>>>>>>> + signed
> >>>>>>>> but %s hash of image is found in DBX.\n", mHashTypeStr));
> >>>>>>>>>> +        IsVerified = FALSE;
> >>>>>>>>>> +        break;
> >>>>>>>>>>        }
> >>>>>>>>>> +
> >>>>>>>>>> +      if (!IsVerified) {
> >>>>>>>>>> +        DbStatus = IsSignatureFoundInDatabase (
> >>>>>>>>>> +                     EFI_IMAGE_SECURITY_DATABASE,
> >>>>>>>>>> +                     mImageDigest,
> >>>>>>>>>> +                     &mCertType,
> >>>>>>>>>> +                     mImageDigestSize,
> >>>>>>>>>> +                     &IsFound
> >>>>>>>>>> +                     );
> >>>>>>>>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
> >>>>>>>>>> +          IsVerified = TRUE;
> >>>>>>>>>> +        } else {
> >>>>>>>>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
> >>>>>>>>>> + signed
> >>>>>> but
> >>>>>>>> signature is not allowed by DB and %s hash of image is not found in
> >>>>>> DB/DBX.\n",
> >>>>>>>> mHashTypeStr));
> >>>>>>>>>> +        }
> >>>>>>>>>> +      }
> >>>>>>>>>> +    }
> >>>>>>>>>> +
> >>>>>>>>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
> >>>>>>>>>> +    if (EFI_ERROR (AddStatus)) {
> >>>>>>>>>> +      break;
> >>>>>>>>>>      }
> >>>>>>>>>> +    OffSet = Result;
> >>>>>>>>>>    }
> >>>>>>>>>>
> >>>>>>>>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
> >>>>>>>>>> {
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> There are other (smaller) reasons why I dislike this patch:
> >>>>>>>>>
> >>>>>>>>> - The "IsAuthDataAssigned" variable is superfluous; we could use
> >>>>>>>>> the existent "AuthData" variable (with a NULL-check and a
> >>>>>>>>> NULL-assignment) similarly.
> >>>>>>>>>
> >>>>>>>>> - The patch complicates / reorganizes the control flow needlessly.
> >>>>>>>>> This complication originates from placing the checked "OffSet"
> >>>>>>>>> increment at the bottom of the loop, which then requires the
> >>>>>>>>> removal of all the "continue" statements. But we don't need to
> >>>>>>>>> check-and-increment at the bottom. We can keep the increment
> >>>>>>>>> inside the "for" statement, only extend the *existent* room check
> >>>>>>>>> (which I've quoted) to take the alignment into account as well. If
> >>>>>>>>> there is enough room for the alignment in the security data
> >>>>>>>>> directory, then that guarantees there won't be a UINT32 overflow
> >> either.
> >>>>>>>>>
> >>>>>>>>> All in all, I'm proposing the following three patches instead. The
> >>>>>>>>> first two patches are preparation, the last patch is the fix.
> >>>>>>>>>
> >>>>>>>>> Patch#1:
> >>>>>>>>>
> >>>>>>>>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep 17
> >>>>>> 00:00:00
> >>>>>>>> 2001
> >>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
> >>>>>>>>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib: extract
> >>>>>>>>>> SecDataDirEnd, SecDataDirLeft
> >>>>>>>>>>
> >>>>>>>>>> The following two quantities:
> >>>>>>>>>>
> >>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
> >>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
> >>>>>>>>>>
> >>>>>>>>>> are used multiple times in DxeImageVerificationHandler().
> >>>>>>>>>> Introduce helper variables for them: "SecDataDirEnd" and
> >> "SecDataDirLeft", respectively.
> >>>>>>>>>> This saves us multiple calculations and significantly simplifies the
> code.
> >>>>>>>>>>
> >>>>>>>>>> Note that all three summands above have type UINT32, therefore
> >>>>>>>>>> the new variables are also of type UINT32.
> >>>>>>>>>>
> >>>>>>>>>> This patch does not change behavior.
> >>>>>>>>>>
> >>>>>>>>>> (Note that the code already handles the case when the
> >>>>>>>>>>
> >>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
> >>>>>>>>>>
> >>>>>>>>>> UINT32 addition overflows -- namely, in that case, the
> >>>>>>>>>> certificate loop is never entered, and the corruption check right
> >>>>>>>>>> after the loop fires.)
> >>>>>>>>>>
> >>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>> ---
> >>>>>>>>>>
> >>>>>>>>>>
> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.c |
> >>>>>> 12
> >>>>>>>> ++++++++----
> >>>>>>>>>>  1 file changed, 8 insertions(+), 4 deletions(-)
> >>>>>>>>>>
> >>>>>>>>>> diff --git
> >>>>>>>>
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.c
> >>>>>>>>
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.c
> >>>>>>>>>> index 36b87e16d53d..8761980c88aa 100644
> >>>>>>>>>> ---
> >>>>>>
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
> >>>>>> .c
> >>>>>>>>>> +++
> >>>>>>>>
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.c
> >>>>>>>>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
> >>>>>>>>>>    UINT8                                *AuthData;
> >>>>>>>>>>    UINTN                                AuthDataSize;
> >>>>>>>>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
> >>>>>>>>>> +  UINT32                               SecDataDirEnd;
> >>>>>>>>>> +  UINT32                               SecDataDirLeft;
> >>>>>>>>>>    UINT32                               OffSet;
> >>>>>>>>>>    CHAR16                               *NameStr;
> >>>>>>>>>>    RETURN_STATUS                        PeCoffStatus;
> >>>>>>>>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
> >>>>>>>>>>    // "Attribute Certificate Table".
> >>>>>>>>>>    // The first certificate starts at offset
> >>>>>>>>>> (SecDataDir->VirtualAddress) from
> >>>>>> the
> >>>>>>>> start of the file.
> >>>>>>>>>>    //
> >>>>>>>>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
> >>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>>>>>>>> +       OffSet < SecDataDirEnd;
> >>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >>>>>>>>>> (WinCertificate-
> >>>>>>>>> dwLength))) {
> >>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <=
> >> sizeof
> >>>>>>>> (WIN_CERTIFICATE) ||
> >>>>>>>>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> >>>>>>>> WinCertificate->dwLength) {
> >>>>>>>>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
> >>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
> >>>>>>>>>> +        SecDataDirLeft < WinCertificate->dwLength) {
> >>>>>>>>>>        break;
> >>>>>>>>>>      }
> >>>>>>>>>>
> >>>>>>>>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
> >>>>>>>>>>      }
> >>>>>>>>>>    }
> >>>>>>>>>>
> >>>>>>>>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
> >>>>>>>>>> {
> >>>>>>>>>> +  if (OffSet != SecDataDirEnd) {
> >>>>>>>>>>      //
> >>>>>>>>>>      // The Size in Certificate Table or the attribute
> >>>>>>>>>> certificate table is
> >>>>>> corrupted.
> >>>>>>>>>>      //
> >>>>>>>>>> --
> >>>>>>>>>> 2.19.1.3.g30247aa5d201
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> Patch#2:
> >>>>>>>>>
> >>>>>>>>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep 17
> >> 00:00:00
> >>>>>>>> 2001
> >>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
> >>>>>>>>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib: assign
> >>>>>>>>>> WinCertificate after size check
> >>>>>>>>>>
> >>>>>>>>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) check
> >>>>>>>>>> only guards the de-referencing of the "WinCertificate" pointer.
> >>>>>>>>>> It does not guard the calculation of hte pointer itself:
> >>>>>>>>>>
> >>>>>>>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>>>>
> >>>>>>>>>> This is wrong; if we don't know for sure that we have enough room
> >>>>>>>>>> for a WIN_CERTIFICATE, then even creating such a pointer, not
> >>>>>>>>>> just de-referencing it, may invoke undefined behavior.
> >>>>>>>>>>
> >>>>>>>>>> Move the pointer calculation after the size check.
> >>>>>>>>>>
> >>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>> ---
> >>>>>>>>>>
> >>>>>>>>>>
> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.c |
> >>>>>> 8
> >>>>>>>> +++++---
> >>>>>>>>>>  1 file changed, 5 insertions(+), 3 deletions(-)
> >>>>>>>>>>
> >>>>>>>>>> diff --git
> >>>>>>>>
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.c
> >>>>>>>>
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.c
> >>>>>>>>>> index 8761980c88aa..461ed7cfb5ac 100644
> >>>>>>>>>> ---
> >>>>>>
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
> >>>>>> .c
> >>>>>>>>>> +++
> >>>>>>>>
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.c
> >>>>>>>>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
> >>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>>>>>>>>         OffSet < SecDataDirEnd;
> >>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >>>>>>>>>> (WinCertificate-
> >>>>>>>>> dwLength))) {
> >>>>>>>>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
> >>>>>>>>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
> >>>>>>>>>> -        SecDataDirLeft < WinCertificate->dwLength) {
> >>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
> >>>>>>>>>> +      break;
> >>>>>>>>>> +    }
> >>>>>>>>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
> >>>>>>>>>>        break;
> >>>>>>>>>>      }
> >>>>>>>>>>
> >>>>>>>>>> --
> >>>>>>>>>> 2.19.1.3.g30247aa5d201
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> Patch#3:
> >>>>>>>>>
> >>>>>>>>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17
> >> 00:00:00
> >>>>>>>> 2001
> >>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
> >>>>>>>>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch
> >>>>>> alignment
> >>>>>>>>>>  overflow (CVE-2019-14562)
> >>>>>>>>>>
> >>>>>>>>>> The DxeImageVerificationHandler() function currently checks
> >>>>>>>>>> whether "SecDataDir" has enough room for
> >>>>>>>>>> "WinCertificate->dwLength". However,
> >>>>>>>> for
> >>>>>>>>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to the
> >>>>>>>>>> next multiple of 8. If "WinCertificate->dwLength" is large
> >>>>>>>>>> enough, the alignment will return 0, and "OffSet" will be stuck at
> the
> >> same value.
> >>>>>>>>>>
> >>>>>>>>>> Check whether "SecDataDir" has room left for both
> >>>>>>>>>> "WinCertificate->dwLength" and the alignment.
> >>>>>>>>>>
> >>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>> ---
> >>>>>>>>>>
> >>>>>>>>>>
> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.c |
> >>>>>> 4
> >>>>>>>> +++-
> >>>>>>>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
> >>>>>>>>>>
> >>>>>>>>>> diff --git
> >>>>>>>>
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.c
> >>>>>>>>
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.c
> >>>>>>>>>> index 461ed7cfb5ac..e38eb981b7a0 100644
> >>>>>>>>>> ---
> >>>>>>
> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
> >>>>>> .c
> >>>>>>>>>> +++
> >>>>>>>>
> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>> ib.c
> >>>>>>>>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
> >>>>>>>>>>        break;
> >>>>>>>>>>      }
> >>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
> >>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
> >>>>>>>>>> +        (SecDataDirLeft - WinCertificate->dwLength <
> >>>>>>>>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
> >>>>>>>>>>        break;
> >>>>>>>>>>      }
> >>>>>>>>>>
> >>>>>>>>>> --
> >>>>>>>>>> 2.19.1.3.g30247aa5d201
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> If Wenyi and the reviewers are OK with these patches, I can submit
> >>>>>>>>> them as a standalone patch series.
> >>>>>>>>>
> >>>>>>>>> Note that I do not have any reproducer for the issue; the best
> >>>>>>>>> testing that I could offer would be some light-weight Secure Boot
> >>>>>>>>> regression tests.
> >>>>>>>>>
> >>>>>>>>> Thanks
> >>>>>>>>> Laszlo
> >>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> .
> >>>>>>>>>
> >>>>>>>>
> >>>>>>>>
> >>>>>>>>
> >>>>>>>
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>>
> >>>>
> >>>
> >>>
> >>> .
> >>>
> >>
> >>
> >>
> >
> 
> 
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-28  6:43                         ` Yao, Jiewen
@ 2020-08-31 11:23                           ` wenyi,xie
  2020-08-31 16:06                             ` Yao, Jiewen
  0 siblings, 1 reply; 32+ messages in thread
From: wenyi,xie @ 2020-08-31 11:23 UTC (permalink / raw)
  To: Yao, Jiewen, devel@edk2.groups.io, Laszlo Ersek, Wang, Jian J
  Cc: songdongkuang@huawei.com, Mathews, John

Hi,Jiewen,

I modify the PE file again, this time it can pass the check in PeCoffLib and cause offset overflow.

First, create a PE file and sign it(only one signature), then using binary edit tool to modify content of PE file like below,
 1.check the value of SecDataDir->VirtualAddress, in my PE file, it's 0xE000
 2.changing SecDataDir->Size from 0x5F8 to 0xFFFF1FFC
 3.changing WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFB
 4.padding PE file with 0 until the size of the file is 0xFFFFFFFC(it will make the PE file so large)
OffSet + WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength) is 0xE000 + 0xFFFF1FFB + 0x5 = 0x100000000

Below is the DEBUG code and log, in second loop the offset overflow and become 0

for (OffSet = SecDataDir->VirtualAddress;
     OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
     OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) {
  WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
  DEBUG((DEBUG_INFO, "OffSet=0x%x.\n", OffSet));
  if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof (WIN_CERTIFICATE) ||
      (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate->dwLength) {
    break;
  }
  DEBUG((DEBUG_INFO, "WinCertificate->dwLength=0x%x, ALIGN_SIZE (WinCertificate->dwLength)=0x%x.\n", WinCertificate->dwLength, ALIGN_SIZE(WinCertificate->dwLength)));


SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFC.
OffSet=0xE000.
WinCertificate->dwLength=0xFFFF1FFB, ALIGN_SIZE (WinCertificate->dwLength)=0x5.
DxeImageVerificationLib: Image is signed but signature is not allowed by DB and SHA256 hash of image is notOffSet=0x0.
WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate->dwLength)=0x3.
OffSet=0x5A50.
WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate->dwLength)=0x4.
OffSet=0xEA78.
WinCertificate->dwLength=0x0, ALIGN_SIZE (WinCertificate->dwLength)=0x0.
The image doesn't pass verification: VenHw(5CF32E0B-8EDF-2E44-9CDA-93205E99EC1C,00000000)/VenHw(964E5B22-6459-11D2-8E39-00A0C969723B,00000000)/\signed_1234_4G.efi


Regards
Wenyi


On 2020/8/28 14:43, Yao, Jiewen wrote:
> Apology that I did not say clearly.
> I mean you should not modify any code to perform an attack.
> 
> I am not asking you to exploit the system. Attack here just means: to cause system hang or buffer overflow. That is enough.
> But you cannot modify code to remove any existing checker.
> 
> Thank you
> Yao Jiewen
> 
>> -----Original Message-----
>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of wenyi,xie
>> via groups.io
>> Sent: Friday, August 28, 2020 2:18 PM
>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Laszlo Ersek
>> <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>
>> Cc: songdongkuang@huawei.com; Mathews, John <john.mathews@intel.com>
>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>
>> Hi,Jiewen,
>>
>> I don't really get the meaning "create a case that bypass all checks in PeCoffLib",
>> do you mean I need to create a PE file that can bypass all check in PeCoffLib
>> without modify any
>> code and then cause the problem in DxeImageVerificationLib, or just modify
>> some code in PeCoffLib to bypass check instead of removing the calling of
>> PeCoffLoaderGetImageInfo. Would
>> you mind explaining a little more specifically? As far as I tried, it's really hard to
>> reproduce the issue without touching any code.
>>
>> Thanks
>> Wenyi
>>
>> On 2020/8/28 11:50, Yao, Jiewen wrote:
>>> HI Wenyi
>>> Thank you very much to take time to reproduce.
>>>
>>> I am particular interested in below:
>>> 	"As PE file is modified, function PeCoffLoaderGetImageInfo will return
>> error, so I have to remove it so that for loop can be tested in
>> DxeImageVerificationHandler."
>>>
>>> By design, the PeCoffLib should catch illegal PE/COFF image and return error.
>> (even it cannot catch all, it should catch most ones).
>>> Other PE/COFF parser may rely on the checker in PeCoffLib and *no need* to
>> duplicate all checkers.
>>> As such, DxeImageVerificationLib (and other PeCoff consumer) just need
>> checks what has passed the check in PeCoffLib.
>>>
>>> I don’t think you should remove the checker. If people can remove the checker,
>> I am sure the rest code will be vulnerable, according to the current design.
>>> Could you please to create a case that bypass all checks in PeCoffLib, then run
>> into DxeImageVerificationLib and cause the problem? That would be more
>> valuable for us.
>>>
>>> Thank you
>>> Yao Jiewen
>>>
>>>> -----Original Message-----
>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>> wenyi,xie
>>>> via groups.io
>>>> Sent: Friday, August 28, 2020 11:18 AM
>>>> To: Laszlo Ersek <lersek@redhat.com>; Wang, Jian J
>> <jian.j.wang@intel.com>;
>>>> devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>
>>>> Cc: songdongkuang@huawei.com; Mathews, John
>> <john.mathews@intel.com>
>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>
>>>> Hi,Laszlo and everyone,
>>>>
>>>> These days I tried to reproduce the issue,and made some progress. I think
>>>> there are two way to cause overflow from a mathematical point of view,
>>>> 1.As Laszlo analysed before, if WinCertificate->dwLength is large enough,
>> close
>>>> to MAX_UINT32, then (WinCertificate->dwLength + ALIGN_SIZE
>> (WinCertificate-
>>>>> dwLength)) will cause overflow.
>>>> 2.(WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength)) is
>> good,
>>>> OffSet is good, but OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>> (WinCertificate->dwLength)) cause overflow.
>>>>
>>>> Here I choose the second way to reproduce the issue, I choose a PE file and
>> sign
>>>> it with my own db certificate. Then I use binary edit tool to modify the PE file
>> like
>>>> below,
>>>>
>>>> 1.change SecDataDir->Size from 0x5F8 to 0xFFFF1FFF
>>>> 2.change WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFE
>>>> SecDataDir->VirtualAddress in my PE is 0xe000 and no need to change.
>>>>
>>>> As PE file is modified, function PeCoffLoaderGetImageInfo will return error,
>> so I
>>>> have to remove it so that for loop can be tested in
>> DxeImageVerificationHandler.
>>>> The log is as below,
>>>>
>>>> SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFF.
>>>> (First Loop)
>>>> OffSet=0xE000.
>>>> WinCertificate->dwLength=0xFFFF1FFE, ALIGN_SIZE (WinCertificate-
>>>>> dwLength)=0x2.
>>>> (Second Loop)
>>>> OffSet=0x0.
>>>> WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate-
>>>>> dwLength)=0x3.
>>>> (Third Loop)
>>>> OffSet=0x5A50.
>>>> WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate-
>>>>> dwLength)=0x4.
>>>> (Forth Loop)
>>>> OffSet=0xEA78.
>>>> WinCertificate->dwLength=0xAFAFAFAF, ALIGN_SIZE (WinCertificate-
>>>>> dwLength)=0x1.
>>>> (Fifth Loop)
>>>> OffSet=0xAFB09A28.
>>>>
>>>> As I modify SecDataDir->Size and WinCertificate->dwLength, so in first loop
>> the
>>>> condition check whether the Security Data Directory has enough room left
>> for
>>>> "WinCertificate->dwLength" is ok.
>>>>
>>>> if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>>>> (WIN_CERTIFICATE) ||
>>>>     (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
>>>>> dwLength) {
>>>>   break;
>>>> }
>>>>
>>>> In the beginning of second loop, WinCertificate->dwLength + ALIGN_SIZE
>>>> (WinCertificate->dwLength) is 0xFFFF2000, offset is 0xE000
>>>>
>>>> OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>> dwLength))
>>>>
>>>> Offset now is 0 and overflow happens. So even if my PE only have one
>> signature,
>>>> the for loop is still going untill exception happens.
>>>>
>>>>
>>>> I can't reproduce the issue using the first way, because if WinCertificate-
>>>>> dwLength is close to MAX_UINT32, it means SecDataDir->Size should also
>> close
>>>> to MAX_UINT32, or the condition check
>>>> "(SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
>>>>> dwLength" will break. But if SecDataDir->Size is very large, SecDataDir-
>>>>> VirtualAddress have to be smaller than 8 bytes,
>>>> because SecDataDir->VirtualAddress + SecDataDir->Size < MAX_UINT32.
>>>> SecDataDir->VirtualAddress is the beginning address of the signature, and
>> before
>>>> SecDataDir->VirtualAddress is the content of origin PE file, I think it's
>> impossible
>>>> that the size of PE file is only 8 bytes.
>>>>
>>>> As I changed the one line code in DxeImageVerificationHandler to reproduce
>> the
>>>> issue, I'm not sure whether it's ok.
>>>>
>>>> Thanks
>>>> Wenyi
>>>>
>>>> On 2020/8/19 17:26, Laszlo Ersek wrote:
>>>>> On 08/18/20 17:18, Mathews, John wrote:
>>>>>> I dug up the original report details.  This was noted as a concern during a
>>>> source code inspection.  There was no demonstration of how it might be
>>>> triggered.
>>>>>>
>>>>>> " There is an integer overflow vulnerability in the
>>>> DxeImageVerificationHandler function when
>>>>>> parsing the PE files attribute certificate table. In cases where
>> WinCertificate-
>>>>> dwLength is
>>>>>> sufficiently large, it's possible to overflow Offset back to 0 causing an
>> endless
>>>> loop."
>>>>>>
>>>>>> The recommendation was to add stricter checking of "Offset" and the
>>>> embedded length fields of certificate data
>>>>>> before using them.
>>>>>
>>>>> Thanks for checking!
>>>>>
>>>>> Laszlo
>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> -----Original Message-----
>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>> Sent: Tuesday, August 18, 2020 1:59 AM
>>>>>> To: Wang, Jian J <jian.j.wang@intel.com>; devel@edk2.groups.io; Yao,
>>>> Jiewen <jiewen.yao@intel.com>; xiewenyi2@huawei.com
>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com; Mathews,
>>>> John <john.mathews@intel.com>
>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>
>>>>>> On 08/18/20 04:10, Wang, Jian J wrote:
>>>>>>> Laszlo,
>>>>>>>
>>>>>>> My apologies for the slow response. I'm not the original reporter but
>>>>>>> just the BZ submitter. And I didn't do deep analysis to this issue.
>>>>>>> The issues was reported from one internal team. Add John in loop to see
>> if
>>>> he knows more about it or not.
>>>>>>>
>>>>>>> My superficial understanding on such issue is that, if there's
>>>>>>> "potential" issue in theory and hard to reproduce, it's still worthy
>>>>>>> of using an alternative way to replace the original implementation
>>>>>>> with no "potential" issue at all. Maybe we don't have to prove old way is
>>>> something wrong but must prove that the new way is really safe.
>>>>>>
>>>>>> I agree, thanks.
>>>>>>
>>>>>> It would be nice to hear more from the internal team about the originally
>>>> reported (even if hard-to-trigger) issue.
>>>>>>
>>>>>> Thanks!
>>>>>> Laszlo
>>>>>>
>>>>>>>
>>>>>>> Regards,
>>>>>>> Jian
>>>>>>>
>>>>>>>> -----Original Message-----
>>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>> Laszlo
>>>>>>>> Ersek
>>>>>>>> Sent: Tuesday, August 18, 2020 12:53 AM
>>>>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
>>>>>>>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>>>
>>>>>>>> Hi Jiewen,
>>>>>>>>
>>>>>>>> On 08/14/20 10:53, Yao, Jiewen wrote:
>>>>>>>>>> To Jiewen,
>>>>>>>>>> Sorry, I don't have environment to reproduce the issue.
>>>>>>>>>
>>>>>>>>> Please help me understand, if you don’t have environment to
>>>>>>>>> reproduce the
>>>>>>>> issue, how do you guarantee that your patch does fix the problem and
>>>>>>>> we don’t have any other vulnerabilities?
>>>>>>>>
>>>>>>>> The original bug report in
>>>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is seriously
>>>>>>>> lacking. It does not go into detail about the alleged integer overflow.
>>>>>>>> It does not quote the code, does not explain the control flow, does
>>>>>>>> not identify the exact edk2 commit at which the vulnerability exists.
>>>>>>>>
>>>>>>>> The bug report also does not offer a reproducer.
>>>>>>>>
>>>>>>>> Additionally, the exact statement that the bug report does make,
>>>>>>>> namely
>>>>>>>>
>>>>>>>>   it's possible to overflow Offset back to 0 causing an endless loop
>>>>>>>>
>>>>>>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can
>>>>>>>> be overflowed to zero, but the *addend* that is added to OffSet can
>>>>>>>> be overflowed to zero. Therefore the infinite loop will arise because
>>>>>>>> OffSet remains stuck at its present value, and not because OffSet
>>>>>>>> will be re-set to zero.
>>>>>>>>
>>>>>>>> For the reasons, we can only speculate as to what the actual problem
>>>>>>>> is, unless Jian decides to join the discussion and clarifies what he
>>>>>>>> had in mind originally.
>>>>>>>>
>>>>>>>> My understanding (or even "reconstruction") of the vulnerability is
>>>>>>>> described above, and in the patches that I proposed.
>>>>>>>>
>>>>>>>> We can write a patch based on code analysis. It's possible to
>>>>>>>> identify integer overflows based on code analysis, and it's possible
>>>>>>>> to verify the correctness of fixes by code review. Obviously testing
>>>>>>>> is always good, but many times, constructing reproducers for such
>>>>>>>> issues that were found by code review, is difficult and time
>>>>>>>> consuming. We can say that we don't fix vulnerabilities without
>>>>>>>> reproducers, or we can say that we make an effort to fix them even if
>>>>>>>> all we have is code analysis (and not a reproducer).
>>>>>>>>
>>>>>>>> So the above paragraph concerns "correctness". Regarding
>>>>>>>> "completeness", I guarantee you that this patch does not fix *all*
>>>>>>>> problems related to PE parsing. (See the other BZ tickets.) It does
>>>>>>>> fix *one* issue with PE parsing. We can say that we try to fix such
>>>>>>>> issues gradually (give different CVE numbers to different issues, and
>>>>>>>> address them one at a time), or we can say that we rewrite PE parsing
>>>> from the ground up.
>>>>>>>> (BTW: I have seriously attempted that in the past, and I gave up,
>>>>>>>> because the PE format is FUBAR.)
>>>>>>>>
>>>>>>>> In summary:
>>>>>>>>
>>>>>>>> - the problem statement is unclear,
>>>>>>>>
>>>>>>>> - it seems like there is indeed an integer overflow problem in the
>>>>>>>> SecDataDir parsing loop, but it's uncertain whether the bug reporter
>>>>>>>> had exactly that in mind
>>>>>>>>
>>>>>>>> - PE parsing is guaranteed to have other vulnerabilities elsewhere in
>>>>>>>> edk2, but I'm currently unaware of other such issues in
>>>>>>>> DxeImageVerificationLib specifically
>>>>>>>>
>>>>>>>> - even if there are other such problems (in DxeImageVerificationLib
>>>>>>>> or elswehere), fixing this bug that we know about is likely
>>>>>>>> worthwhile
>>>>>>>>
>>>>>>>> - for many such bugs, constructing a reproducer is difficult and time
>>>>>>>> consuming; code analysis, and *regression-testing* are frequently the
>>>>>>>> only tools we have. That doesn't mean we should ignore this class of
>> bugs.
>>>>>>>>
>>>>>>>> (Fixing integer overflows retro-actively is more difficult than
>>>>>>>> writing overflow-free code in the first place, but that ship has
>>>>>>>> sailed; so we can only fight these bugs incrementally now, unless we
>>>>>>>> can rewrite PE parsing with a new data structure from the ground up.
>>>>>>>> Again I tried that and gave up, because the spec is not public, and
>>>>>>>> what I did manage to learn about PE, showed that it was insanely
>>>>>>>> over-engineered. I'm not saying that other binary / executable
>>>>>>>> formats are better, of course.)
>>>>>>>>
>>>>>>>> Please check out my patches (inlined elsewhere in this thread), and
>>>>>>>> comment whether you'd like me to post them to the list as a
>>>>>>>> standalone series.
>>>>>>>>
>>>>>>>> Jian: it wouldn't hurt if you commented as well.
>>>>>>>>
>>>>>>>> Thanks
>>>>>>>> Laszlo
>>>>>>>>
>>>>>>>>>> -----Original Message-----
>>>>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>>>>>>>> wenyi,xie
>>>>>>>>>> via groups.io
>>>>>>>>>> Sent: Friday, August 14, 2020 3:54 PM
>>>>>>>>>> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io; Yao,
>>>>>>>>>> Jiewen <jiewen.yao@intel.com>; Wang, Jian J
>> <jian.j.wang@intel.com>
>>>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>>>>>
>>>>>>>>>> To Laszlo,
>>>>>>>>>> Thank you for your detailed description, I agree with what you
>>>>>>>>>> analyzed and
>>>>>>>> I'm
>>>>>>>>>> OK with your patches, it's
>>>>>>>>>> correct and much simpler.
>>>>>>>>>>
>>>>>>>>>> To Jiewen,
>>>>>>>>>> Sorry, I don't have environment to reproduce the issue.
>>>>>>>>>>
>>>>>>>>>> Thanks
>>>>>>>>>> Wenyi
>>>>>>>>>>
>>>>>>>>>> On 2020/8/14 2:50, Laszlo Ersek wrote:
>>>>>>>>>>> On 08/13/20 13:55, Wenyi Xie wrote:
>>>>>>>>>>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
>>>>>>>>>>>>
>>>>>>>>>>>> There is an integer overflow vulnerability in
>>>>>>>>>>>> DxeImageVerificationHandler function when parsing the PE files
>>>>>>>>>>>> attribute certificate table. In cases where
>>>>>>>>>>>> WinCertificate->dwLength is sufficiently large, it's possible to
>>>> overflow Offset back to 0 causing an endless loop.
>>>>>>>>>>>>
>>>>>>>>>>>> Check offset inbetween VirtualAddress and VirtualAddress + Size.
>>>>>>>>>>>> Using SafeintLib to do offset addition with result check.
>>>>>>>>>>>>
>>>>>>>>>>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>>>>>>>>>>>> Cc: Jian J Wang <jian.j.wang@intel.com>
>>>>>>>>>>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
>>>>>>>>>>>> ---
>>>>>>>>>>>>
>>>>>>>>>>>>
>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.inf
>>>>>>>> |
>>>>>>>>>> 1 +
>>>>>>>>>>>>
>>>>>>>>>>>>
>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.h
>>>>>>>> |
>>>>>>>>>> 1 +
>>>>>>>>>>>>
>>>>>>>>>>>>
>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>> |
>>>>>>>>>> 111 +++++++++++---------
>>>>>>>>>>>>  3 files changed, 63 insertions(+), 50 deletions(-)
>>>>>>>>>>>>
>>>>>>>>>>>> diff --git
>>>>>>>>>>
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.inf
>>>>>>>>>>
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.inf
>>>>>>>>>>>> index 1e1a639857e0..a7ac4830b3d4 100644
>>>>>>>>>>>> ---
>>>>>>>>>>
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.inf
>>>>>>>>>>>> +++
>>>>>>>>>>
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.inf
>>>>>>>>>>>> @@ -53,6 +53,7 @@ [LibraryClasses]
>>>>>>>>>>>>    SecurityManagementLib
>>>>>>>>>>>>    PeCoffLib
>>>>>>>>>>>>    TpmMeasurementLib
>>>>>>>>>>>> +  SafeIntLib
>>>>>>>>>>>>
>>>>>>>>>>>>  [Protocols]
>>>>>>>>>>>>    gEfiFirmwareVolume2ProtocolGuid       ##
>> SOMETIMES_CONSUMES
>>>>>>>>>>>> diff --git
>>>>>>>>>>
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.h
>>>>>>>>>>
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.h
>>>>>>>>>>>> index 17955ff9774c..060273917d5d 100644
>>>>>>>>>>>> ---
>>>>>>>>>>
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.h
>>>>>>>>>>>> +++
>>>>>>>>>>
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.h
>>>>>>>>>>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
>>>>>>>>>>>> #include <Library/DevicePathLib.h>  #include
>>>>>>>>>>>> <Library/SecurityManagementLib.h>  #include <Library/PeCoffLib.h>
>>>>>>>>>>>> +#include <Library/SafeIntLib.h>
>>>>>>>>>>>>  #include <Protocol/FirmwareVolume2.h>  #include
>>>>>>>>>>>> <Protocol/DevicePath.h>  #include <Protocol/BlockIo.h> diff --git
>>>>>>>>>>
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.c
>>>>>>>>>>
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.c
>>>>>>>>>>>> index 36b87e16d53d..dbc03e28c05b 100644
>>>>>>>>>>>> ---
>>>>>>>>
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>> .c
>>>>>>>>>>>> +++
>>>>>>>>>>
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.c
>>>>>>>>>>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>    EFI_STATUS                           HashStatus;
>>>>>>>>>>>>    EFI_STATUS                           DbStatus;
>>>>>>>>>>>>    BOOLEAN                              IsFound;
>>>>>>>>>>>> +  UINT32                               AlignedLength;
>>>>>>>>>>>> +  UINT32                               Result;
>>>>>>>>>>>> +  EFI_STATUS                           AddStatus;
>>>>>>>>>>>> +  BOOLEAN                              IsAuthDataAssigned;
>>>>>>>>>>>>
>>>>>>>>>>>>    SignatureList     = NULL;
>>>>>>>>>>>>    SignatureListSize = 0;
>>>>>>>>>>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
>>>>>>>>>>>>    IsVerified        = FALSE;
>>>>>>>>>>>>    IsFound           = FALSE;
>>>>>>>>>>>> +  Result            = 0;
>>>>>>>>>>>>
>>>>>>>>>>>>    //
>>>>>>>>>>>>    // Check the image type and get policy setting.
>>>>>>>>>>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>    // The first certificate starts at offset
>>>>>>>>>>>> (SecDataDir->VirtualAddress) from
>>>>>>>> the
>>>>>>>>>> start of the file.
>>>>>>>>>>>>    //
>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>> (WinCertificate-
>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet <
>>>>>>>>>>>> + (SecDataDir-
>>>>>>>>>>> VirtualAddress + SecDataDir->Size));) {
>>>>>>>>>>>> +    IsAuthDataAssigned = FALSE;
>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>> (WinCertificate-
>>>>>>>>>>> dwLength);
>>>>>>>>>>>
>>>>>>>>>>> I disagree with this patch.
>>>>>>>>>>>
>>>>>>>>>>> The primary reason for my disagreement is that the bug report
>>>>>>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is
>>>>>>>>>>> inexact, and so this patch tries to fix the wrong thing.
>>>>>>>>>>>
>>>>>>>>>>> With edk2 master at commit 65904cdbb33c, it is *not* possible to
>>>>>>>>>>> overflow the OffSet variable to zero with "WinCertificate-
>>> dwLength"
>>>>>>>>>>> *purely*, and cause an endless loop. Note that we have (at commit
>>>>>>>>>>> 65904cdbb33c):
>>>>>>>>>>>
>>>>>>>>>>>   for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>>> (WinCertificate-
>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>>>> <= sizeof
>>>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>>>>>> WinCertificate-
>>>>>>>>>>> dwLength) {
>>>>>>>>>>>       break;
>>>>>>>>>>>     }
>>>>>>>>>>>
>>>>>>>>>>> The last sub-condition checks whether the Security Data Directory
>>>>>>>>>>> has enough room left for "WinCertificate->dwLength". If not, then
>>>>>>>>>>> we break out of the loop.
>>>>>>>>>>>
>>>>>>>>>>> If we *do* have enough room, that is:
>>>>>>>>>>>
>>>>>>>>>>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >=
>>>>>>>> WinCertificate-
>>>>>>>>>>> dwLength
>>>>>>>>>>>
>>>>>>>>>>> then we have (by adding OffSet to both sides):
>>>>>>>>>>>
>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet +
>>>>>>>>>>> WinCertificate- dwLength
>>>>>>>>>>>
>>>>>>>>>>> The left hand side is a known-good UINT32, and so incrementing
>>>>>>>>>>> OffSet (a
>>>>>>>>>>> UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32)
>>>>>>>>>>> does not cause an overflow.
>>>>>>>>>>>
>>>>>>>>>>> Instead, the problem is with the alignment. The "if" statement
>>>>>>>>>>> checks whether we have enough room for "dwLength", but then
>>>>>>>>>>> "OffSet" is advanced by "dwLength" *aligned up* to the next
>>>>>>>>>>> multiple of 8. And that may indeed cause various overflows.
>>>>>>>>>>>
>>>>>>>>>>> Now, the main problem with the present patch is that it does not
>>>>>>>>>>> fix one of those overflows. Namely, consider that "dwLength" is
>>>>>>>>>>> very close to
>>>>>>>>>>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then aligning
>>>>>>>>>>> it up to the next multiple of 8 will yield 0. In other words,
>>>> "AlignedLength"
>>>>>>>>>>> will be zero.
>>>>>>>>>>>
>>>>>>>>>>> And when that happens, there's going to be an infinite loop just
>>>>>>>>>>> the
>>>>>>>>>>> same: "OffSet" will not be zero, but it will be *stuck*. The
>>>>>>>>>>> SafeUint32Add() call at the bottom will succeed, but it will not
>>>>>>>>>>> change the value of "OffSet".
>>>>>>>>>>>
>>>>>>>>>>> More at the bottom.
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>>>>> <= sizeof
>>>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>>>>> <
>>>>>>>>>> WinCertificate->dwLength) {
>>>>>>>>>>>>        break;
>>>>>>>>>>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>        }
>>>>>>>>>>>>        AuthData   = PkcsCertData->CertData;
>>>>>>>>>>>>        AuthDataSize = PkcsCertData->Hdr.dwLength -
>>>>>>>>>>>> sizeof(PkcsCertData-
>>>>>>>>> Hdr);
>>>>>>>>>>>> +      IsAuthDataAssigned = TRUE;
>>>>>>>>>>>> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>>>>>>>      } else if (WinCertificate->wCertificateType ==
>>>>>>>> WIN_CERT_TYPE_EFI_GUID)
>>>>>>>>>> {
>>>>>>>>>>>>        //
>>>>>>>>>>>>        // The certificate is formatted as
>>>>>>>>>>>> WIN_CERTIFICATE_UEFI_GUID which
>>>>>>>> is
>>>>>>>>>> described in UEFI Spec.
>>>>>>>>>>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>        if (WinCertUefiGuid->Hdr.dwLength <=
>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
>>>>>>>>>>>>          break;
>>>>>>>>>>>>        }
>>>>>>>>>>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType,
>>>> &gEfiCertPkcs7Guid))
>>>>>>>> {
>>>>>>>>>>>> -        continue;
>>>>>>>>>>>> +      if (CompareGuid (&WinCertUefiGuid->CertType,
>>>>>>>>>>>> + &gEfiCertPkcs7Guid))
>>>>>>>> {
>>>>>>>>>>>> +        AuthData = WinCertUefiGuid->CertData;
>>>>>>>>>>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>>>>>>> +        IsAuthDataAssigned = TRUE;
>>>>>>>>>>>> +        HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>>>>>>>        }
>>>>>>>>>>>> -      AuthData = WinCertUefiGuid->CertData;
>>>>>>>>>>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>>>>>>>      } else {
>>>>>>>>>>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
>>>>>>>>>>>>          break;
>>>>>>>>>>>>        }
>>>>>>>>>>>> -      continue;
>>>>>>>>>>>>      }
>>>>>>>>>>>>
>>>>>>>>>>>> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>>>>>>> -    if (EFI_ERROR (HashStatus)) {
>>>>>>>>>>>> -      continue;
>>>>>>>>>>>> -    }
>>>>>>>>>>>> -
>>>>>>>>>>>> -    //
>>>>>>>>>>>> -    // Check the digital signature against the revoked certificate in
>>>>>>>> forbidden
>>>>>>>>>> database (dbx).
>>>>>>>>>>>> -    //
>>>>>>>>>>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>>>>>>>> -      IsVerified = FALSE;
>>>>>>>>>>>> -      break;
>>>>>>>>>>>> -    }
>>>>>>>>>>>> -
>>>>>>>>>>>> -    //
>>>>>>>>>>>> -    // Check the digital signature against the valid certificate in
>>>> allowed
>>>>>>>>>> database (db).
>>>>>>>>>>>> -    //
>>>>>>>>>>>> -    if (!IsVerified) {
>>>>>>>>>>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>>>>>>>> -        IsVerified = TRUE;
>>>>>>>>>>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
>>>>>>>>>>>> +      //
>>>>>>>>>>>> +      // Check the digital signature against the revoked
>>>>>>>>>>>> + certificate in
>>>>>>>> forbidden
>>>>>>>>>> database (dbx).
>>>>>>>>>>>> +      //
>>>>>>>>>>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>>>>>>>> +        IsVerified = FALSE;
>>>>>>>>>>>> +        break;
>>>>>>>>>>>>        }
>>>>>>>>>>>> -    }
>>>>>>>>>>>>
>>>>>>>>>>>> -    //
>>>>>>>>>>>> -    // Check the image's hash value.
>>>>>>>>>>>> -    //
>>>>>>>>>>>> -    DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>>>>>>> -                 mImageDigest,
>>>>>>>>>>>> -                 &mCertType,
>>>>>>>>>>>> -                 mImageDigestSize,
>>>>>>>>>>>> -                 &IsFound
>>>>>>>>>>>> -                 );
>>>>>>>>>>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>>>>>>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
>>>> signed
>>>>>>>> but %s
>>>>>>>>>> hash of image is found in DBX.\n", mHashTypeStr));
>>>>>>>>>>>> -      IsVerified = FALSE;
>>>>>>>>>>>> -      break;
>>>>>>>>>>>> -    }
>>>>>>>>>>>> +      //
>>>>>>>>>>>> +      // Check the digital signature against the valid
>>>>>>>>>>>> + certificate in allowed
>>>>>>>>>> database (db).
>>>>>>>>>>>> +      //
>>>>>>>>>>>> +      if (!IsVerified) {
>>>>>>>>>>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>>>>>>>> +          IsVerified = TRUE;
>>>>>>>>>>>> +        }
>>>>>>>>>>>> +      }
>>>>>>>>>>>>
>>>>>>>>>>>> -    if (!IsVerified) {
>>>>>>>>>>>> +      //
>>>>>>>>>>>> +      // Check the image's hash value.
>>>>>>>>>>>> +      //
>>>>>>>>>>>>        DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>>>> -                   EFI_IMAGE_SECURITY_DATABASE,
>>>>>>>>>>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>>>>>>>                     mImageDigest,
>>>>>>>>>>>>                     &mCertType,
>>>>>>>>>>>>                     mImageDigestSize,
>>>>>>>>>>>>                     &IsFound
>>>>>>>>>>>>                     );
>>>>>>>>>>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>>>>>>>> -        IsVerified = TRUE;
>>>>>>>>>>>> -      } else {
>>>>>>>>>>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
>>>> signed
>>>>>>>> but
>>>>>>>>>> signature is not allowed by DB and %s hash of image is not found in
>>>>>>>> DB/DBX.\n",
>>>>>>>>>> mHashTypeStr));
>>>>>>>>>>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>>>>>>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
>>>>>>>>>>>> + signed
>>>>>>>>>> but %s hash of image is found in DBX.\n", mHashTypeStr));
>>>>>>>>>>>> +        IsVerified = FALSE;
>>>>>>>>>>>> +        break;
>>>>>>>>>>>>        }
>>>>>>>>>>>> +
>>>>>>>>>>>> +      if (!IsVerified) {
>>>>>>>>>>>> +        DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>>>> +                     EFI_IMAGE_SECURITY_DATABASE,
>>>>>>>>>>>> +                     mImageDigest,
>>>>>>>>>>>> +                     &mCertType,
>>>>>>>>>>>> +                     mImageDigestSize,
>>>>>>>>>>>> +                     &IsFound
>>>>>>>>>>>> +                     );
>>>>>>>>>>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>>>>>>>> +          IsVerified = TRUE;
>>>>>>>>>>>> +        } else {
>>>>>>>>>>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
>>>>>>>>>>>> + signed
>>>>>>>> but
>>>>>>>>>> signature is not allowed by DB and %s hash of image is not found in
>>>>>>>> DB/DBX.\n",
>>>>>>>>>> mHashTypeStr));
>>>>>>>>>>>> +        }
>>>>>>>>>>>> +      }
>>>>>>>>>>>> +    }
>>>>>>>>>>>> +
>>>>>>>>>>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
>>>>>>>>>>>> +    if (EFI_ERROR (AddStatus)) {
>>>>>>>>>>>> +      break;
>>>>>>>>>>>>      }
>>>>>>>>>>>> +    OffSet = Result;
>>>>>>>>>>>>    }
>>>>>>>>>>>>
>>>>>>>>>>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
>>>>>>>>>>>> {
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> There are other (smaller) reasons why I dislike this patch:
>>>>>>>>>>>
>>>>>>>>>>> - The "IsAuthDataAssigned" variable is superfluous; we could use
>>>>>>>>>>> the existent "AuthData" variable (with a NULL-check and a
>>>>>>>>>>> NULL-assignment) similarly.
>>>>>>>>>>>
>>>>>>>>>>> - The patch complicates / reorganizes the control flow needlessly.
>>>>>>>>>>> This complication originates from placing the checked "OffSet"
>>>>>>>>>>> increment at the bottom of the loop, which then requires the
>>>>>>>>>>> removal of all the "continue" statements. But we don't need to
>>>>>>>>>>> check-and-increment at the bottom. We can keep the increment
>>>>>>>>>>> inside the "for" statement, only extend the *existent* room check
>>>>>>>>>>> (which I've quoted) to take the alignment into account as well. If
>>>>>>>>>>> there is enough room for the alignment in the security data
>>>>>>>>>>> directory, then that guarantees there won't be a UINT32 overflow
>>>> either.
>>>>>>>>>>>
>>>>>>>>>>> All in all, I'm proposing the following three patches instead. The
>>>>>>>>>>> first two patches are preparation, the last patch is the fix.
>>>>>>>>>>>
>>>>>>>>>>> Patch#1:
>>>>>>>>>>>
>>>>>>>>>>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep 17
>>>>>>>> 00:00:00
>>>>>>>>>> 2001
>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
>>>>>>>>>>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib: extract
>>>>>>>>>>>> SecDataDirEnd, SecDataDirLeft
>>>>>>>>>>>>
>>>>>>>>>>>> The following two quantities:
>>>>>>>>>>>>
>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
>>>>>>>>>>>>
>>>>>>>>>>>> are used multiple times in DxeImageVerificationHandler().
>>>>>>>>>>>> Introduce helper variables for them: "SecDataDirEnd" and
>>>> "SecDataDirLeft", respectively.
>>>>>>>>>>>> This saves us multiple calculations and significantly simplifies the
>> code.
>>>>>>>>>>>>
>>>>>>>>>>>> Note that all three summands above have type UINT32, therefore
>>>>>>>>>>>> the new variables are also of type UINT32.
>>>>>>>>>>>>
>>>>>>>>>>>> This patch does not change behavior.
>>>>>>>>>>>>
>>>>>>>>>>>> (Note that the code already handles the case when the
>>>>>>>>>>>>
>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>>>>>>>
>>>>>>>>>>>> UINT32 addition overflows -- namely, in that case, the
>>>>>>>>>>>> certificate loop is never entered, and the corruption check right
>>>>>>>>>>>> after the loop fires.)
>>>>>>>>>>>>
>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>> ---
>>>>>>>>>>>>
>>>>>>>>>>>>
>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c |
>>>>>>>> 12
>>>>>>>>>> ++++++++----
>>>>>>>>>>>>  1 file changed, 8 insertions(+), 4 deletions(-)
>>>>>>>>>>>>
>>>>>>>>>>>> diff --git
>>>>>>>>>>
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.c
>>>>>>>>>>
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.c
>>>>>>>>>>>> index 36b87e16d53d..8761980c88aa 100644
>>>>>>>>>>>> ---
>>>>>>>>
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>> .c
>>>>>>>>>>>> +++
>>>>>>>>>>
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.c
>>>>>>>>>>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>    UINT8                                *AuthData;
>>>>>>>>>>>>    UINTN                                AuthDataSize;
>>>>>>>>>>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
>>>>>>>>>>>> +  UINT32                               SecDataDirEnd;
>>>>>>>>>>>> +  UINT32                               SecDataDirLeft;
>>>>>>>>>>>>    UINT32                               OffSet;
>>>>>>>>>>>>    CHAR16                               *NameStr;
>>>>>>>>>>>>    RETURN_STATUS                        PeCoffStatus;
>>>>>>>>>>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>    // "Attribute Certificate Table".
>>>>>>>>>>>>    // The first certificate starts at offset
>>>>>>>>>>>> (SecDataDir->VirtualAddress) from
>>>>>>>> the
>>>>>>>>>> start of the file.
>>>>>>>>>>>>    //
>>>>>>>>>>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>>>> +       OffSet < SecDataDirEnd;
>>>>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>>>> (WinCertificate-
>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <=
>>>> sizeof
>>>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>>>>>>>> WinCertificate->dwLength) {
>>>>>>>>>>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>>>>>>>> +        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>        break;
>>>>>>>>>>>>      }
>>>>>>>>>>>>
>>>>>>>>>>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>      }
>>>>>>>>>>>>    }
>>>>>>>>>>>>
>>>>>>>>>>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
>>>>>>>>>>>> {
>>>>>>>>>>>> +  if (OffSet != SecDataDirEnd) {
>>>>>>>>>>>>      //
>>>>>>>>>>>>      // The Size in Certificate Table or the attribute
>>>>>>>>>>>> certificate table is
>>>>>>>> corrupted.
>>>>>>>>>>>>      //
>>>>>>>>>>>> --
>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Patch#2:
>>>>>>>>>>>
>>>>>>>>>>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep 17
>>>> 00:00:00
>>>>>>>>>> 2001
>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
>>>>>>>>>>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib: assign
>>>>>>>>>>>> WinCertificate after size check
>>>>>>>>>>>>
>>>>>>>>>>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) check
>>>>>>>>>>>> only guards the de-referencing of the "WinCertificate" pointer.
>>>>>>>>>>>> It does not guard the calculation of hte pointer itself:
>>>>>>>>>>>>
>>>>>>>>>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>>>
>>>>>>>>>>>> This is wrong; if we don't know for sure that we have enough room
>>>>>>>>>>>> for a WIN_CERTIFICATE, then even creating such a pointer, not
>>>>>>>>>>>> just de-referencing it, may invoke undefined behavior.
>>>>>>>>>>>>
>>>>>>>>>>>> Move the pointer calculation after the size check.
>>>>>>>>>>>>
>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>> ---
>>>>>>>>>>>>
>>>>>>>>>>>>
>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c |
>>>>>>>> 8
>>>>>>>>>> +++++---
>>>>>>>>>>>>  1 file changed, 5 insertions(+), 3 deletions(-)
>>>>>>>>>>>>
>>>>>>>>>>>> diff --git
>>>>>>>>>>
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.c
>>>>>>>>>>
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.c
>>>>>>>>>>>> index 8761980c88aa..461ed7cfb5ac 100644
>>>>>>>>>>>> ---
>>>>>>>>
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>> .c
>>>>>>>>>>>> +++
>>>>>>>>>>
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.c
>>>>>>>>>>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>>         OffSet < SecDataDirEnd;
>>>>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>>>> (WinCertificate-
>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>>>>>>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>>>>>>>> -        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
>>>>>>>>>>>> +      break;
>>>>>>>>>>>> +    }
>>>>>>>>>>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>        break;
>>>>>>>>>>>>      }
>>>>>>>>>>>>
>>>>>>>>>>>> --
>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Patch#3:
>>>>>>>>>>>
>>>>>>>>>>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17
>>>> 00:00:00
>>>>>>>>>> 2001
>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
>>>>>>>>>>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch
>>>>>>>> alignment
>>>>>>>>>>>>  overflow (CVE-2019-14562)
>>>>>>>>>>>>
>>>>>>>>>>>> The DxeImageVerificationHandler() function currently checks
>>>>>>>>>>>> whether "SecDataDir" has enough room for
>>>>>>>>>>>> "WinCertificate->dwLength". However,
>>>>>>>>>> for
>>>>>>>>>>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to the
>>>>>>>>>>>> next multiple of 8. If "WinCertificate->dwLength" is large
>>>>>>>>>>>> enough, the alignment will return 0, and "OffSet" will be stuck at
>> the
>>>> same value.
>>>>>>>>>>>>
>>>>>>>>>>>> Check whether "SecDataDir" has room left for both
>>>>>>>>>>>> "WinCertificate->dwLength" and the alignment.
>>>>>>>>>>>>
>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>> ---
>>>>>>>>>>>>
>>>>>>>>>>>>
>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c |
>>>>>>>> 4
>>>>>>>>>> +++-
>>>>>>>>>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>>>>>>>>>>
>>>>>>>>>>>> diff --git
>>>>>>>>>>
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.c
>>>>>>>>>>
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.c
>>>>>>>>>>>> index 461ed7cfb5ac..e38eb981b7a0 100644
>>>>>>>>>>>> ---
>>>>>>>>
>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>> .c
>>>>>>>>>>>> +++
>>>>>>>>>>
>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>> ib.c
>>>>>>>>>>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>        break;
>>>>>>>>>>>>      }
>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
>>>>>>>>>>>> +        (SecDataDirLeft - WinCertificate->dwLength <
>>>>>>>>>>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
>>>>>>>>>>>>        break;
>>>>>>>>>>>>      }
>>>>>>>>>>>>
>>>>>>>>>>>> --
>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> If Wenyi and the reviewers are OK with these patches, I can submit
>>>>>>>>>>> them as a standalone patch series.
>>>>>>>>>>>
>>>>>>>>>>> Note that I do not have any reproducer for the issue; the best
>>>>>>>>>>> testing that I could offer would be some light-weight Secure Boot
>>>>>>>>>>> regression tests.
>>>>>>>>>>>
>>>>>>>>>>> Thanks
>>>>>>>>>>> Laszlo
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> .
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>> .
>>>>>
>>>>
>>>>
>>>>
>>>
>>
>>
>> 
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-31 11:23                           ` wenyi,xie
@ 2020-08-31 16:06                             ` Yao, Jiewen
  2020-09-01  7:10                               ` wenyi,xie
  2020-09-01  7:29                               ` Laszlo Ersek
  0 siblings, 2 replies; 32+ messages in thread
From: Yao, Jiewen @ 2020-08-31 16:06 UTC (permalink / raw)
  To: devel@edk2.groups.io, xiewenyi2@huawei.com, Laszlo Ersek,
	Wang, Jian J
  Cc: songdongkuang@huawei.com, Mathews, John

Sounds great. Appreciate your hard work on that.

Will you post a patch to fix the issue again?

Thank you
Yao Jiewen

> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of wenyi,xie
> via groups.io
> Sent: Monday, August 31, 2020 7:24 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Laszlo Ersek
> <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>
> Cc: songdongkuang@huawei.com; Mathews, John <john.mathews@intel.com>
> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> 
> Hi,Jiewen,
> 
> I modify the PE file again, this time it can pass the check in PeCoffLib and cause
> offset overflow.
> 
> First, create a PE file and sign it(only one signature), then using binary edit tool
> to modify content of PE file like below,
>  1.check the value of SecDataDir->VirtualAddress, in my PE file, it's 0xE000
>  2.changing SecDataDir->Size from 0x5F8 to 0xFFFF1FFC
>  3.changing WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFB
>  4.padding PE file with 0 until the size of the file is 0xFFFFFFFC(it will make the PE
> file so large)
> OffSet + WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength) is
> 0xE000 + 0xFFFF1FFB + 0x5 = 0x100000000
> 
> Below is the DEBUG code and log, in second loop the offset overflow and
> become 0
> 
> for (OffSet = SecDataDir->VirtualAddress;
>      OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>      OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >dwLength))) {
>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>   DEBUG((DEBUG_INFO, "OffSet=0x%x.\n", OffSet));
>   if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
> (WIN_CERTIFICATE) ||
>       (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
> >dwLength) {
>     break;
>   }
>   DEBUG((DEBUG_INFO, "WinCertificate->dwLength=0x%x, ALIGN_SIZE
> (WinCertificate->dwLength)=0x%x.\n", WinCertificate->dwLength,
> ALIGN_SIZE(WinCertificate->dwLength)));
> 
> 
> SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFC.
> OffSet=0xE000.
> WinCertificate->dwLength=0xFFFF1FFB, ALIGN_SIZE (WinCertificate-
> >dwLength)=0x5.
> DxeImageVerificationLib: Image is signed but signature is not allowed by DB and
> SHA256 hash of image is notOffSet=0x0.
> WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate-
> >dwLength)=0x3.
> OffSet=0x5A50.
> WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate-
> >dwLength)=0x4.
> OffSet=0xEA78.
> WinCertificate->dwLength=0x0, ALIGN_SIZE (WinCertificate->dwLength)=0x0.
> The image doesn't pass verification: VenHw(5CF32E0B-8EDF-2E44-9CDA-
> 93205E99EC1C,00000000)/VenHw(964E5B22-6459-11D2-8E39-
> 00A0C969723B,00000000)/\signed_1234_4G.efi
> 
> 
> Regards
> Wenyi
> 
> 
> On 2020/8/28 14:43, Yao, Jiewen wrote:
> > Apology that I did not say clearly.
> > I mean you should not modify any code to perform an attack.
> >
> > I am not asking you to exploit the system. Attack here just means: to cause
> system hang or buffer overflow. That is enough.
> > But you cannot modify code to remove any existing checker.
> >
> > Thank you
> > Yao Jiewen
> >
> >> -----Original Message-----
> >> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
> wenyi,xie
> >> via groups.io
> >> Sent: Friday, August 28, 2020 2:18 PM
> >> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Laszlo
> Ersek
> >> <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>
> >> Cc: songdongkuang@huawei.com; Mathews, John
> <john.mathews@intel.com>
> >> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> >>
> >> Hi,Jiewen,
> >>
> >> I don't really get the meaning "create a case that bypass all checks in
> PeCoffLib",
> >> do you mean I need to create a PE file that can bypass all check in PeCoffLib
> >> without modify any
> >> code and then cause the problem in DxeImageVerificationLib, or just modify
> >> some code in PeCoffLib to bypass check instead of removing the calling of
> >> PeCoffLoaderGetImageInfo. Would
> >> you mind explaining a little more specifically? As far as I tried, it's really hard
> to
> >> reproduce the issue without touching any code.
> >>
> >> Thanks
> >> Wenyi
> >>
> >> On 2020/8/28 11:50, Yao, Jiewen wrote:
> >>> HI Wenyi
> >>> Thank you very much to take time to reproduce.
> >>>
> >>> I am particular interested in below:
> >>> 	"As PE file is modified, function PeCoffLoaderGetImageInfo will return
> >> error, so I have to remove it so that for loop can be tested in
> >> DxeImageVerificationHandler."
> >>>
> >>> By design, the PeCoffLib should catch illegal PE/COFF image and return error.
> >> (even it cannot catch all, it should catch most ones).
> >>> Other PE/COFF parser may rely on the checker in PeCoffLib and *no need*
> to
> >> duplicate all checkers.
> >>> As such, DxeImageVerificationLib (and other PeCoff consumer) just need
> >> checks what has passed the check in PeCoffLib.
> >>>
> >>> I don’t think you should remove the checker. If people can remove the
> checker,
> >> I am sure the rest code will be vulnerable, according to the current design.
> >>> Could you please to create a case that bypass all checks in PeCoffLib, then
> run
> >> into DxeImageVerificationLib and cause the problem? That would be more
> >> valuable for us.
> >>>
> >>> Thank you
> >>> Yao Jiewen
> >>>
> >>>> -----Original Message-----
> >>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
> >> wenyi,xie
> >>>> via groups.io
> >>>> Sent: Friday, August 28, 2020 11:18 AM
> >>>> To: Laszlo Ersek <lersek@redhat.com>; Wang, Jian J
> >> <jian.j.wang@intel.com>;
> >>>> devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>
> >>>> Cc: songdongkuang@huawei.com; Mathews, John
> >> <john.mathews@intel.com>
> >>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> >>>>
> >>>> Hi,Laszlo and everyone,
> >>>>
> >>>> These days I tried to reproduce the issue,and made some progress. I
> think
> >>>> there are two way to cause overflow from a mathematical point of view,
> >>>> 1.As Laszlo analysed before, if WinCertificate->dwLength is large enough,
> >> close
> >>>> to MAX_UINT32, then (WinCertificate->dwLength + ALIGN_SIZE
> >> (WinCertificate-
> >>>>> dwLength)) will cause overflow.
> >>>> 2.(WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength)) is
> >> good,
> >>>> OffSet is good, but OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >>>> (WinCertificate->dwLength)) cause overflow.
> >>>>
> >>>> Here I choose the second way to reproduce the issue, I choose a PE file
> and
> >> sign
> >>>> it with my own db certificate. Then I use binary edit tool to modify the PE
> file
> >> like
> >>>> below,
> >>>>
> >>>> 1.change SecDataDir->Size from 0x5F8 to 0xFFFF1FFF
> >>>> 2.change WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFE
> >>>> SecDataDir->VirtualAddress in my PE is 0xe000 and no need to change.
> >>>>
> >>>> As PE file is modified, function PeCoffLoaderGetImageInfo will return error,
> >> so I
> >>>> have to remove it so that for loop can be tested in
> >> DxeImageVerificationHandler.
> >>>> The log is as below,
> >>>>
> >>>> SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFF.
> >>>> (First Loop)
> >>>> OffSet=0xE000.
> >>>> WinCertificate->dwLength=0xFFFF1FFE, ALIGN_SIZE (WinCertificate-
> >>>>> dwLength)=0x2.
> >>>> (Second Loop)
> >>>> OffSet=0x0.
> >>>> WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate-
> >>>>> dwLength)=0x3.
> >>>> (Third Loop)
> >>>> OffSet=0x5A50.
> >>>> WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate-
> >>>>> dwLength)=0x4.
> >>>> (Forth Loop)
> >>>> OffSet=0xEA78.
> >>>> WinCertificate->dwLength=0xAFAFAFAF, ALIGN_SIZE (WinCertificate-
> >>>>> dwLength)=0x1.
> >>>> (Fifth Loop)
> >>>> OffSet=0xAFB09A28.
> >>>>
> >>>> As I modify SecDataDir->Size and WinCertificate->dwLength, so in first
> loop
> >> the
> >>>> condition check whether the Security Data Directory has enough room left
> >> for
> >>>> "WinCertificate->dwLength" is ok.
> >>>>
> >>>> if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
> >>>> (WIN_CERTIFICATE) ||
> >>>>     (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> WinCertificate-
> >>>>> dwLength) {
> >>>>   break;
> >>>> }
> >>>>
> >>>> In the beginning of second loop, WinCertificate->dwLength + ALIGN_SIZE
> >>>> (WinCertificate->dwLength) is 0xFFFF2000, offset is 0xE000
> >>>>
> >>>> OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >>> dwLength))
> >>>>
> >>>> Offset now is 0 and overflow happens. So even if my PE only have one
> >> signature,
> >>>> the for loop is still going untill exception happens.
> >>>>
> >>>>
> >>>> I can't reproduce the issue using the first way, because if WinCertificate-
> >>>>> dwLength is close to MAX_UINT32, it means SecDataDir->Size should also
> >> close
> >>>> to MAX_UINT32, or the condition check
> >>>> "(SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> WinCertificate-
> >>>>> dwLength" will break. But if SecDataDir->Size is very large, SecDataDir-
> >>>>> VirtualAddress have to be smaller than 8 bytes,
> >>>> because SecDataDir->VirtualAddress + SecDataDir->Size < MAX_UINT32.
> >>>> SecDataDir->VirtualAddress is the beginning address of the signature, and
> >> before
> >>>> SecDataDir->VirtualAddress is the content of origin PE file, I think it's
> >> impossible
> >>>> that the size of PE file is only 8 bytes.
> >>>>
> >>>> As I changed the one line code in DxeImageVerificationHandler to
> reproduce
> >> the
> >>>> issue, I'm not sure whether it's ok.
> >>>>
> >>>> Thanks
> >>>> Wenyi
> >>>>
> >>>> On 2020/8/19 17:26, Laszlo Ersek wrote:
> >>>>> On 08/18/20 17:18, Mathews, John wrote:
> >>>>>> I dug up the original report details.  This was noted as a concern during a
> >>>> source code inspection.  There was no demonstration of how it might be
> >>>> triggered.
> >>>>>>
> >>>>>> " There is an integer overflow vulnerability in the
> >>>> DxeImageVerificationHandler function when
> >>>>>> parsing the PE files attribute certificate table. In cases where
> >> WinCertificate-
> >>>>> dwLength is
> >>>>>> sufficiently large, it's possible to overflow Offset back to 0 causing an
> >> endless
> >>>> loop."
> >>>>>>
> >>>>>> The recommendation was to add stricter checking of "Offset" and the
> >>>> embedded length fields of certificate data
> >>>>>> before using them.
> >>>>>
> >>>>> Thanks for checking!
> >>>>>
> >>>>> Laszlo
> >>>>>
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>>> -----Original Message-----
> >>>>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>>>> Sent: Tuesday, August 18, 2020 1:59 AM
> >>>>>> To: Wang, Jian J <jian.j.wang@intel.com>; devel@edk2.groups.io; Yao,
> >>>> Jiewen <jiewen.yao@intel.com>; xiewenyi2@huawei.com
> >>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com;
> Mathews,
> >>>> John <john.mathews@intel.com>
> >>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> >>>>>>
> >>>>>> On 08/18/20 04:10, Wang, Jian J wrote:
> >>>>>>> Laszlo,
> >>>>>>>
> >>>>>>> My apologies for the slow response. I'm not the original reporter but
> >>>>>>> just the BZ submitter. And I didn't do deep analysis to this issue.
> >>>>>>> The issues was reported from one internal team. Add John in loop to
> see
> >> if
> >>>> he knows more about it or not.
> >>>>>>>
> >>>>>>> My superficial understanding on such issue is that, if there's
> >>>>>>> "potential" issue in theory and hard to reproduce, it's still worthy
> >>>>>>> of using an alternative way to replace the original implementation
> >>>>>>> with no "potential" issue at all. Maybe we don't have to prove old way
> is
> >>>> something wrong but must prove that the new way is really safe.
> >>>>>>
> >>>>>> I agree, thanks.
> >>>>>>
> >>>>>> It would be nice to hear more from the internal team about the
> originally
> >>>> reported (even if hard-to-trigger) issue.
> >>>>>>
> >>>>>> Thanks!
> >>>>>> Laszlo
> >>>>>>
> >>>>>>>
> >>>>>>> Regards,
> >>>>>>> Jian
> >>>>>>>
> >>>>>>>> -----Original Message-----
> >>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
> >> Laszlo
> >>>>>>>> Ersek
> >>>>>>>> Sent: Tuesday, August 18, 2020 12:53 AM
> >>>>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
> >>>>>>>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
> >>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
> >>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> >>>>>>>>
> >>>>>>>> Hi Jiewen,
> >>>>>>>>
> >>>>>>>> On 08/14/20 10:53, Yao, Jiewen wrote:
> >>>>>>>>>> To Jiewen,
> >>>>>>>>>> Sorry, I don't have environment to reproduce the issue.
> >>>>>>>>>
> >>>>>>>>> Please help me understand, if you don’t have environment to
> >>>>>>>>> reproduce the
> >>>>>>>> issue, how do you guarantee that your patch does fix the problem and
> >>>>>>>> we don’t have any other vulnerabilities?
> >>>>>>>>
> >>>>>>>> The original bug report in
> >>>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is
> seriously
> >>>>>>>> lacking. It does not go into detail about the alleged integer overflow.
> >>>>>>>> It does not quote the code, does not explain the control flow, does
> >>>>>>>> not identify the exact edk2 commit at which the vulnerability exists.
> >>>>>>>>
> >>>>>>>> The bug report also does not offer a reproducer.
> >>>>>>>>
> >>>>>>>> Additionally, the exact statement that the bug report does make,
> >>>>>>>> namely
> >>>>>>>>
> >>>>>>>>   it's possible to overflow Offset back to 0 causing an endless loop
> >>>>>>>>
> >>>>>>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can
> >>>>>>>> be overflowed to zero, but the *addend* that is added to OffSet can
> >>>>>>>> be overflowed to zero. Therefore the infinite loop will arise because
> >>>>>>>> OffSet remains stuck at its present value, and not because OffSet
> >>>>>>>> will be re-set to zero.
> >>>>>>>>
> >>>>>>>> For the reasons, we can only speculate as to what the actual problem
> >>>>>>>> is, unless Jian decides to join the discussion and clarifies what he
> >>>>>>>> had in mind originally.
> >>>>>>>>
> >>>>>>>> My understanding (or even "reconstruction") of the vulnerability is
> >>>>>>>> described above, and in the patches that I proposed.
> >>>>>>>>
> >>>>>>>> We can write a patch based on code analysis. It's possible to
> >>>>>>>> identify integer overflows based on code analysis, and it's possible
> >>>>>>>> to verify the correctness of fixes by code review. Obviously testing
> >>>>>>>> is always good, but many times, constructing reproducers for such
> >>>>>>>> issues that were found by code review, is difficult and time
> >>>>>>>> consuming. We can say that we don't fix vulnerabilities without
> >>>>>>>> reproducers, or we can say that we make an effort to fix them even if
> >>>>>>>> all we have is code analysis (and not a reproducer).
> >>>>>>>>
> >>>>>>>> So the above paragraph concerns "correctness". Regarding
> >>>>>>>> "completeness", I guarantee you that this patch does not fix *all*
> >>>>>>>> problems related to PE parsing. (See the other BZ tickets.) It does
> >>>>>>>> fix *one* issue with PE parsing. We can say that we try to fix such
> >>>>>>>> issues gradually (give different CVE numbers to different issues, and
> >>>>>>>> address them one at a time), or we can say that we rewrite PE parsing
> >>>> from the ground up.
> >>>>>>>> (BTW: I have seriously attempted that in the past, and I gave up,
> >>>>>>>> because the PE format is FUBAR.)
> >>>>>>>>
> >>>>>>>> In summary:
> >>>>>>>>
> >>>>>>>> - the problem statement is unclear,
> >>>>>>>>
> >>>>>>>> - it seems like there is indeed an integer overflow problem in the
> >>>>>>>> SecDataDir parsing loop, but it's uncertain whether the bug reporter
> >>>>>>>> had exactly that in mind
> >>>>>>>>
> >>>>>>>> - PE parsing is guaranteed to have other vulnerabilities elsewhere in
> >>>>>>>> edk2, but I'm currently unaware of other such issues in
> >>>>>>>> DxeImageVerificationLib specifically
> >>>>>>>>
> >>>>>>>> - even if there are other such problems (in DxeImageVerificationLib
> >>>>>>>> or elswehere), fixing this bug that we know about is likely
> >>>>>>>> worthwhile
> >>>>>>>>
> >>>>>>>> - for many such bugs, constructing a reproducer is difficult and time
> >>>>>>>> consuming; code analysis, and *regression-testing* are frequently the
> >>>>>>>> only tools we have. That doesn't mean we should ignore this class of
> >> bugs.
> >>>>>>>>
> >>>>>>>> (Fixing integer overflows retro-actively is more difficult than
> >>>>>>>> writing overflow-free code in the first place, but that ship has
> >>>>>>>> sailed; so we can only fight these bugs incrementally now, unless we
> >>>>>>>> can rewrite PE parsing with a new data structure from the ground up.
> >>>>>>>> Again I tried that and gave up, because the spec is not public, and
> >>>>>>>> what I did manage to learn about PE, showed that it was insanely
> >>>>>>>> over-engineered. I'm not saying that other binary / executable
> >>>>>>>> formats are better, of course.)
> >>>>>>>>
> >>>>>>>> Please check out my patches (inlined elsewhere in this thread), and
> >>>>>>>> comment whether you'd like me to post them to the list as a
> >>>>>>>> standalone series.
> >>>>>>>>
> >>>>>>>> Jian: it wouldn't hurt if you commented as well.
> >>>>>>>>
> >>>>>>>> Thanks
> >>>>>>>> Laszlo
> >>>>>>>>
> >>>>>>>>>> -----Original Message-----
> >>>>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf
> Of
> >>>>>>>> wenyi,xie
> >>>>>>>>>> via groups.io
> >>>>>>>>>> Sent: Friday, August 14, 2020 3:54 PM
> >>>>>>>>>> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io; Yao,
> >>>>>>>>>> Jiewen <jiewen.yao@intel.com>; Wang, Jian J
> >> <jian.j.wang@intel.com>
> >>>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
> >>>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >>>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of
> Offset
> >>>>>>>>>>
> >>>>>>>>>> To Laszlo,
> >>>>>>>>>> Thank you for your detailed description, I agree with what you
> >>>>>>>>>> analyzed and
> >>>>>>>> I'm
> >>>>>>>>>> OK with your patches, it's
> >>>>>>>>>> correct and much simpler.
> >>>>>>>>>>
> >>>>>>>>>> To Jiewen,
> >>>>>>>>>> Sorry, I don't have environment to reproduce the issue.
> >>>>>>>>>>
> >>>>>>>>>> Thanks
> >>>>>>>>>> Wenyi
> >>>>>>>>>>
> >>>>>>>>>> On 2020/8/14 2:50, Laszlo Ersek wrote:
> >>>>>>>>>>> On 08/13/20 13:55, Wenyi Xie wrote:
> >>>>>>>>>>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
> >>>>>>>>>>>>
> >>>>>>>>>>>> There is an integer overflow vulnerability in
> >>>>>>>>>>>> DxeImageVerificationHandler function when parsing the PE files
> >>>>>>>>>>>> attribute certificate table. In cases where
> >>>>>>>>>>>> WinCertificate->dwLength is sufficiently large, it's possible to
> >>>> overflow Offset back to 0 causing an endless loop.
> >>>>>>>>>>>>
> >>>>>>>>>>>> Check offset inbetween VirtualAddress and VirtualAddress + Size.
> >>>>>>>>>>>> Using SafeintLib to do offset addition with result check.
> >>>>>>>>>>>>
> >>>>>>>>>>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
> >>>>>>>>>>>> Cc: Jian J Wang <jian.j.wang@intel.com>
> >>>>>>>>>>>> Cc: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
> >>>>>>>>>>>> ---
> >>>>>>>>>>>>
> >>>>>>>>>>>>
> >> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.inf
> >>>>>>>> |
> >>>>>>>>>> 1 +
> >>>>>>>>>>>>
> >>>>>>>>>>>>
> >> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.h
> >>>>>>>> |
> >>>>>>>>>> 1 +
> >>>>>>>>>>>>
> >>>>>>>>>>>>
> >> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.c
> >>>>>>>> |
> >>>>>>>>>> 111 +++++++++++---------
> >>>>>>>>>>>>  3 files changed, 63 insertions(+), 50 deletions(-)
> >>>>>>>>>>>>
> >>>>>>>>>>>> diff --git
> >>>>>>>>>>
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.inf
> >>>>>>>>>>
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.inf
> >>>>>>>>>>>> index 1e1a639857e0..a7ac4830b3d4 100644
> >>>>>>>>>>>> ---
> >>>>>>>>>>
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.inf
> >>>>>>>>>>>> +++
> >>>>>>>>>>
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.inf
> >>>>>>>>>>>> @@ -53,6 +53,7 @@ [LibraryClasses]
> >>>>>>>>>>>>    SecurityManagementLib
> >>>>>>>>>>>>    PeCoffLib
> >>>>>>>>>>>>    TpmMeasurementLib
> >>>>>>>>>>>> +  SafeIntLib
> >>>>>>>>>>>>
> >>>>>>>>>>>>  [Protocols]
> >>>>>>>>>>>>    gEfiFirmwareVolume2ProtocolGuid       ##
> >> SOMETIMES_CONSUMES
> >>>>>>>>>>>> diff --git
> >>>>>>>>>>
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.h
> >>>>>>>>>>
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.h
> >>>>>>>>>>>> index 17955ff9774c..060273917d5d 100644
> >>>>>>>>>>>> ---
> >>>>>>>>>>
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.h
> >>>>>>>>>>>> +++
> >>>>>>>>>>
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.h
> >>>>>>>>>>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-
> Patent
> >>>>>>>>>>>> #include <Library/DevicePathLib.h>  #include
> >>>>>>>>>>>> <Library/SecurityManagementLib.h>  #include
> <Library/PeCoffLib.h>
> >>>>>>>>>>>> +#include <Library/SafeIntLib.h>
> >>>>>>>>>>>>  #include <Protocol/FirmwareVolume2.h>  #include
> >>>>>>>>>>>> <Protocol/DevicePath.h>  #include <Protocol/BlockIo.h> diff --
> git
> >>>>>>>>>>
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.c
> >>>>>>>>>>
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.c
> >>>>>>>>>>>> index 36b87e16d53d..dbc03e28c05b 100644
> >>>>>>>>>>>> ---
> >>>>>>>>
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
> >>>>>>>> .c
> >>>>>>>>>>>> +++
> >>>>>>>>>>
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.c
> >>>>>>>>>>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>    EFI_STATUS                           HashStatus;
> >>>>>>>>>>>>    EFI_STATUS                           DbStatus;
> >>>>>>>>>>>>    BOOLEAN                              IsFound;
> >>>>>>>>>>>> +  UINT32                               AlignedLength;
> >>>>>>>>>>>> +  UINT32                               Result;
> >>>>>>>>>>>> +  EFI_STATUS                           AddStatus;
> >>>>>>>>>>>> +  BOOLEAN                              IsAuthDataAssigned;
> >>>>>>>>>>>>
> >>>>>>>>>>>>    SignatureList     = NULL;
> >>>>>>>>>>>>    SignatureListSize = 0;
> >>>>>>>>>>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
> >>>>>>>>>>>>    IsVerified        = FALSE;
> >>>>>>>>>>>>    IsFound           = FALSE;
> >>>>>>>>>>>> +  Result            = 0;
> >>>>>>>>>>>>
> >>>>>>>>>>>>    //
> >>>>>>>>>>>>    // Check the image type and get policy setting.
> >>>>>>>>>>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>    // The first certificate starts at offset
> >>>>>>>>>>>> (SecDataDir->VirtualAddress) from
> >>>>>>>> the
> >>>>>>>>>> start of the file.
> >>>>>>>>>>>>    //
> >>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>>>>>>>>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >>>> (WinCertificate-
> >>>>>>>>>>> dwLength))) {
> >>>>>>>>>>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet <
> >>>>>>>>>>>> + (SecDataDir-
> >>>>>>>>>>> VirtualAddress + SecDataDir->Size));) {
> >>>>>>>>>>>> +    IsAuthDataAssigned = FALSE;
> >>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>>>>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE
> >>>>>>>> (WinCertificate-
> >>>>>>>>>>> dwLength);
> >>>>>>>>>>>
> >>>>>>>>>>> I disagree with this patch.
> >>>>>>>>>>>
> >>>>>>>>>>> The primary reason for my disagreement is that the bug report
> >>>>>>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is
> >>>>>>>>>>> inexact, and so this patch tries to fix the wrong thing.
> >>>>>>>>>>>
> >>>>>>>>>>> With edk2 master at commit 65904cdbb33c, it is *not* possible to
> >>>>>>>>>>> overflow the OffSet variable to zero with "WinCertificate-
> >>> dwLength"
> >>>>>>>>>>> *purely*, and cause an endless loop. Note that we have (at
> commit
> >>>>>>>>>>> 65904cdbb33c):
> >>>>>>>>>>>
> >>>>>>>>>>>   for (OffSet = SecDataDir->VirtualAddress;
> >>>>>>>>>>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>>>>>>>>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >>>>>>>>>>> (WinCertificate-
> >>>>>>>>>>> dwLength))) {
> >>>>>>>>>>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>>>>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
> >>>>>>>>>>> <= sizeof
> >>>>>>>>>> (WIN_CERTIFICATE) ||
> >>>>>>>>>>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> >>>>>>>> WinCertificate-
> >>>>>>>>>>> dwLength) {
> >>>>>>>>>>>       break;
> >>>>>>>>>>>     }
> >>>>>>>>>>>
> >>>>>>>>>>> The last sub-condition checks whether the Security Data Directory
> >>>>>>>>>>> has enough room left for "WinCertificate->dwLength". If not, then
> >>>>>>>>>>> we break out of the loop.
> >>>>>>>>>>>
> >>>>>>>>>>> If we *do* have enough room, that is:
> >>>>>>>>>>>
> >>>>>>>>>>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >=
> >>>>>>>> WinCertificate-
> >>>>>>>>>>> dwLength
> >>>>>>>>>>>
> >>>>>>>>>>> then we have (by adding OffSet to both sides):
> >>>>>>>>>>>
> >>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet +
> >>>>>>>>>>> WinCertificate- dwLength
> >>>>>>>>>>>
> >>>>>>>>>>> The left hand side is a known-good UINT32, and so incrementing
> >>>>>>>>>>> OffSet (a
> >>>>>>>>>>> UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32)
> >>>>>>>>>>> does not cause an overflow.
> >>>>>>>>>>>
> >>>>>>>>>>> Instead, the problem is with the alignment. The "if" statement
> >>>>>>>>>>> checks whether we have enough room for "dwLength", but then
> >>>>>>>>>>> "OffSet" is advanced by "dwLength" *aligned up* to the next
> >>>>>>>>>>> multiple of 8. And that may indeed cause various overflows.
> >>>>>>>>>>>
> >>>>>>>>>>> Now, the main problem with the present patch is that it does not
> >>>>>>>>>>> fix one of those overflows. Namely, consider that "dwLength" is
> >>>>>>>>>>> very close to
> >>>>>>>>>>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then
> aligning
> >>>>>>>>>>> it up to the next multiple of 8 will yield 0. In other words,
> >>>> "AlignedLength"
> >>>>>>>>>>> will be zero.
> >>>>>>>>>>>
> >>>>>>>>>>> And when that happens, there's going to be an infinite loop just
> >>>>>>>>>>> the
> >>>>>>>>>>> same: "OffSet" will not be zero, but it will be *stuck*. The
> >>>>>>>>>>> SafeUint32Add() call at the bottom will succeed, but it will not
> >>>>>>>>>>> change the value of "OffSet".
> >>>>>>>>>>>
> >>>>>>>>>>> More at the bottom.
> >>>>>>>>>>>
> >>>>>>>>>>>
> >>>>>>>>>>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
> >>>>>>>>>>>> <= sizeof
> >>>>>>>>>> (WIN_CERTIFICATE) ||
> >>>>>>>>>>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
> >>>>>>>>>>>> <
> >>>>>>>>>> WinCertificate->dwLength) {
> >>>>>>>>>>>>        break;
> >>>>>>>>>>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>        }
> >>>>>>>>>>>>        AuthData   = PkcsCertData->CertData;
> >>>>>>>>>>>>        AuthDataSize = PkcsCertData->Hdr.dwLength -
> >>>>>>>>>>>> sizeof(PkcsCertData-
> >>>>>>>>> Hdr);
> >>>>>>>>>>>> +      IsAuthDataAssigned = TRUE;
> >>>>>>>>>>>> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> >>>>>>>>>>>>      } else if (WinCertificate->wCertificateType ==
> >>>>>>>> WIN_CERT_TYPE_EFI_GUID)
> >>>>>>>>>> {
> >>>>>>>>>>>>        //
> >>>>>>>>>>>>        // The certificate is formatted as
> >>>>>>>>>>>> WIN_CERTIFICATE_UEFI_GUID which
> >>>>>>>> is
> >>>>>>>>>> described in UEFI Spec.
> >>>>>>>>>>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>        if (WinCertUefiGuid->Hdr.dwLength <=
> >>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
> >>>>>>>>>>>>          break;
> >>>>>>>>>>>>        }
> >>>>>>>>>>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType,
> >>>> &gEfiCertPkcs7Guid))
> >>>>>>>> {
> >>>>>>>>>>>> -        continue;
> >>>>>>>>>>>> +      if (CompareGuid (&WinCertUefiGuid->CertType,
> >>>>>>>>>>>> + &gEfiCertPkcs7Guid))
> >>>>>>>> {
> >>>>>>>>>>>> +        AuthData = WinCertUefiGuid->CertData;
> >>>>>>>>>>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
> >>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
> >>>>>>>>>>>> +        IsAuthDataAssigned = TRUE;
> >>>>>>>>>>>> +        HashStatus = HashPeImageByType (AuthData,
> AuthDataSize);
> >>>>>>>>>>>>        }
> >>>>>>>>>>>> -      AuthData = WinCertUefiGuid->CertData;
> >>>>>>>>>>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
> >>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
> >>>>>>>>>>>>      } else {
> >>>>>>>>>>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
> >>>>>>>>>>>>          break;
> >>>>>>>>>>>>        }
> >>>>>>>>>>>> -      continue;
> >>>>>>>>>>>>      }
> >>>>>>>>>>>>
> >>>>>>>>>>>> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
> >>>>>>>>>>>> -    if (EFI_ERROR (HashStatus)) {
> >>>>>>>>>>>> -      continue;
> >>>>>>>>>>>> -    }
> >>>>>>>>>>>> -
> >>>>>>>>>>>> -    //
> >>>>>>>>>>>> -    // Check the digital signature against the revoked certificate
> in
> >>>>>>>> forbidden
> >>>>>>>>>> database (dbx).
> >>>>>>>>>>>> -    //
> >>>>>>>>>>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> >>>>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> >>>>>>>>>>>> -      IsVerified = FALSE;
> >>>>>>>>>>>> -      break;
> >>>>>>>>>>>> -    }
> >>>>>>>>>>>> -
> >>>>>>>>>>>> -    //
> >>>>>>>>>>>> -    // Check the digital signature against the valid certificate in
> >>>> allowed
> >>>>>>>>>> database (db).
> >>>>>>>>>>>> -    //
> >>>>>>>>>>>> -    if (!IsVerified) {
> >>>>>>>>>>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
> >>>>>>>>>>>> -        IsVerified = TRUE;
> >>>>>>>>>>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
> >>>>>>>>>>>> +      //
> >>>>>>>>>>>> +      // Check the digital signature against the revoked
> >>>>>>>>>>>> + certificate in
> >>>>>>>> forbidden
> >>>>>>>>>> database (dbx).
> >>>>>>>>>>>> +      //
> >>>>>>>>>>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> >>>>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> >>>>>>>>>>>> +        IsVerified = FALSE;
> >>>>>>>>>>>> +        break;
> >>>>>>>>>>>>        }
> >>>>>>>>>>>> -    }
> >>>>>>>>>>>>
> >>>>>>>>>>>> -    //
> >>>>>>>>>>>> -    // Check the image's hash value.
> >>>>>>>>>>>> -    //
> >>>>>>>>>>>> -    DbStatus = IsSignatureFoundInDatabase (
> >>>>>>>>>>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
> >>>>>>>>>>>> -                 mImageDigest,
> >>>>>>>>>>>> -                 &mCertType,
> >>>>>>>>>>>> -                 mImageDigestSize,
> >>>>>>>>>>>> -                 &IsFound
> >>>>>>>>>>>> -                 );
> >>>>>>>>>>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
> >>>>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> >>>>>>>>>>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
> >>>> signed
> >>>>>>>> but %s
> >>>>>>>>>> hash of image is found in DBX.\n", mHashTypeStr));
> >>>>>>>>>>>> -      IsVerified = FALSE;
> >>>>>>>>>>>> -      break;
> >>>>>>>>>>>> -    }
> >>>>>>>>>>>> +      //
> >>>>>>>>>>>> +      // Check the digital signature against the valid
> >>>>>>>>>>>> + certificate in allowed
> >>>>>>>>>> database (db).
> >>>>>>>>>>>> +      //
> >>>>>>>>>>>> +      if (!IsVerified) {
> >>>>>>>>>>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
> >>>>>>>>>>>> +          IsVerified = TRUE;
> >>>>>>>>>>>> +        }
> >>>>>>>>>>>> +      }
> >>>>>>>>>>>>
> >>>>>>>>>>>> -    if (!IsVerified) {
> >>>>>>>>>>>> +      //
> >>>>>>>>>>>> +      // Check the image's hash value.
> >>>>>>>>>>>> +      //
> >>>>>>>>>>>>        DbStatus = IsSignatureFoundInDatabase (
> >>>>>>>>>>>> -                   EFI_IMAGE_SECURITY_DATABASE,
> >>>>>>>>>>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
> >>>>>>>>>>>>                     mImageDigest,
> >>>>>>>>>>>>                     &mCertType,
> >>>>>>>>>>>>                     mImageDigestSize,
> >>>>>>>>>>>>                     &IsFound
> >>>>>>>>>>>>                     );
> >>>>>>>>>>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
> >>>>>>>>>>>> -        IsVerified = TRUE;
> >>>>>>>>>>>> -      } else {
> >>>>>>>>>>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
> >>>> signed
> >>>>>>>> but
> >>>>>>>>>> signature is not allowed by DB and %s hash of image is not found in
> >>>>>>>> DB/DBX.\n",
> >>>>>>>>>> mHashTypeStr));
> >>>>>>>>>>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
> >>>>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> >>>>>>>>>>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
> >>>>>>>>>>>> + signed
> >>>>>>>>>> but %s hash of image is found in DBX.\n", mHashTypeStr));
> >>>>>>>>>>>> +        IsVerified = FALSE;
> >>>>>>>>>>>> +        break;
> >>>>>>>>>>>>        }
> >>>>>>>>>>>> +
> >>>>>>>>>>>> +      if (!IsVerified) {
> >>>>>>>>>>>> +        DbStatus = IsSignatureFoundInDatabase (
> >>>>>>>>>>>> +                     EFI_IMAGE_SECURITY_DATABASE,
> >>>>>>>>>>>> +                     mImageDigest,
> >>>>>>>>>>>> +                     &mCertType,
> >>>>>>>>>>>> +                     mImageDigestSize,
> >>>>>>>>>>>> +                     &IsFound
> >>>>>>>>>>>> +                     );
> >>>>>>>>>>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
> >>>>>>>>>>>> +          IsVerified = TRUE;
> >>>>>>>>>>>> +        } else {
> >>>>>>>>>>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image
> is
> >>>>>>>>>>>> + signed
> >>>>>>>> but
> >>>>>>>>>> signature is not allowed by DB and %s hash of image is not found in
> >>>>>>>> DB/DBX.\n",
> >>>>>>>>>> mHashTypeStr));
> >>>>>>>>>>>> +        }
> >>>>>>>>>>>> +      }
> >>>>>>>>>>>> +    }
> >>>>>>>>>>>> +
> >>>>>>>>>>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
> >>>>>>>>>>>> +    if (EFI_ERROR (AddStatus)) {
> >>>>>>>>>>>> +      break;
> >>>>>>>>>>>>      }
> >>>>>>>>>>>> +    OffSet = Result;
> >>>>>>>>>>>>    }
> >>>>>>>>>>>>
> >>>>>>>>>>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
> >>>>>>>>>>>> {
> >>>>>>>>>>>>
> >>>>>>>>>>>
> >>>>>>>>>>> There are other (smaller) reasons why I dislike this patch:
> >>>>>>>>>>>
> >>>>>>>>>>> - The "IsAuthDataAssigned" variable is superfluous; we could use
> >>>>>>>>>>> the existent "AuthData" variable (with a NULL-check and a
> >>>>>>>>>>> NULL-assignment) similarly.
> >>>>>>>>>>>
> >>>>>>>>>>> - The patch complicates / reorganizes the control flow needlessly.
> >>>>>>>>>>> This complication originates from placing the checked "OffSet"
> >>>>>>>>>>> increment at the bottom of the loop, which then requires the
> >>>>>>>>>>> removal of all the "continue" statements. But we don't need to
> >>>>>>>>>>> check-and-increment at the bottom. We can keep the increment
> >>>>>>>>>>> inside the "for" statement, only extend the *existent* room check
> >>>>>>>>>>> (which I've quoted) to take the alignment into account as well. If
> >>>>>>>>>>> there is enough room for the alignment in the security data
> >>>>>>>>>>> directory, then that guarantees there won't be a UINT32 overflow
> >>>> either.
> >>>>>>>>>>>
> >>>>>>>>>>> All in all, I'm proposing the following three patches instead. The
> >>>>>>>>>>> first two patches are preparation, the last patch is the fix.
> >>>>>>>>>>>
> >>>>>>>>>>> Patch#1:
> >>>>>>>>>>>
> >>>>>>>>>>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep
> 17
> >>>>>>>> 00:00:00
> >>>>>>>>>> 2001
> >>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
> >>>>>>>>>>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib:
> extract
> >>>>>>>>>>>> SecDataDirEnd, SecDataDirLeft
> >>>>>>>>>>>>
> >>>>>>>>>>>> The following two quantities:
> >>>>>>>>>>>>
> >>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
> >>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
> >>>>>>>>>>>>
> >>>>>>>>>>>> are used multiple times in DxeImageVerificationHandler().
> >>>>>>>>>>>> Introduce helper variables for them: "SecDataDirEnd" and
> >>>> "SecDataDirLeft", respectively.
> >>>>>>>>>>>> This saves us multiple calculations and significantly simplifies the
> >> code.
> >>>>>>>>>>>>
> >>>>>>>>>>>> Note that all three summands above have type UINT32,
> therefore
> >>>>>>>>>>>> the new variables are also of type UINT32.
> >>>>>>>>>>>>
> >>>>>>>>>>>> This patch does not change behavior.
> >>>>>>>>>>>>
> >>>>>>>>>>>> (Note that the code already handles the case when the
> >>>>>>>>>>>>
> >>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
> >>>>>>>>>>>>
> >>>>>>>>>>>> UINT32 addition overflows -- namely, in that case, the
> >>>>>>>>>>>> certificate loop is never entered, and the corruption check right
> >>>>>>>>>>>> after the loop fires.)
> >>>>>>>>>>>>
> >>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>>>> ---
> >>>>>>>>>>>>
> >>>>>>>>>>>>
> >> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.c |
> >>>>>>>> 12
> >>>>>>>>>> ++++++++----
> >>>>>>>>>>>>  1 file changed, 8 insertions(+), 4 deletions(-)
> >>>>>>>>>>>>
> >>>>>>>>>>>> diff --git
> >>>>>>>>>>
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.c
> >>>>>>>>>>
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.c
> >>>>>>>>>>>> index 36b87e16d53d..8761980c88aa 100644
> >>>>>>>>>>>> ---
> >>>>>>>>
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
> >>>>>>>> .c
> >>>>>>>>>>>> +++
> >>>>>>>>>>
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.c
> >>>>>>>>>>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>    UINT8                                *AuthData;
> >>>>>>>>>>>>    UINTN                                AuthDataSize;
> >>>>>>>>>>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
> >>>>>>>>>>>> +  UINT32                               SecDataDirEnd;
> >>>>>>>>>>>> +  UINT32                               SecDataDirLeft;
> >>>>>>>>>>>>    UINT32                               OffSet;
> >>>>>>>>>>>>    CHAR16                               *NameStr;
> >>>>>>>>>>>>    RETURN_STATUS                        PeCoffStatus;
> >>>>>>>>>>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>    // "Attribute Certificate Table".
> >>>>>>>>>>>>    // The first certificate starts at offset
> >>>>>>>>>>>> (SecDataDir->VirtualAddress) from
> >>>>>>>> the
> >>>>>>>>>> start of the file.
> >>>>>>>>>>>>    //
> >>>>>>>>>>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir-
> >Size;
> >>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>>>>>>>>>> +       OffSet < SecDataDirEnd;
> >>>>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >>>>>>>>>>>> (WinCertificate-
> >>>>>>>>>>> dwLength))) {
> >>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>>>>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <=
> >>>> sizeof
> >>>>>>>>>> (WIN_CERTIFICATE) ||
> >>>>>>>>>>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> >>>>>>>>>> WinCertificate->dwLength) {
> >>>>>>>>>>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
> >>>>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
> >>>>>>>>>>>> +        SecDataDirLeft < WinCertificate->dwLength) {
> >>>>>>>>>>>>        break;
> >>>>>>>>>>>>      }
> >>>>>>>>>>>>
> >>>>>>>>>>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>      }
> >>>>>>>>>>>>    }
> >>>>>>>>>>>>
> >>>>>>>>>>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
> >>>>>>>>>>>> {
> >>>>>>>>>>>> +  if (OffSet != SecDataDirEnd) {
> >>>>>>>>>>>>      //
> >>>>>>>>>>>>      // The Size in Certificate Table or the attribute
> >>>>>>>>>>>> certificate table is
> >>>>>>>> corrupted.
> >>>>>>>>>>>>      //
> >>>>>>>>>>>> --
> >>>>>>>>>>>> 2.19.1.3.g30247aa5d201
> >>>>>>>>>>>>
> >>>>>>>>>>>
> >>>>>>>>>>> Patch#2:
> >>>>>>>>>>>
> >>>>>>>>>>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep
> 17
> >>>> 00:00:00
> >>>>>>>>>> 2001
> >>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
> >>>>>>>>>>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib:
> assign
> >>>>>>>>>>>> WinCertificate after size check
> >>>>>>>>>>>>
> >>>>>>>>>>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE))
> check
> >>>>>>>>>>>> only guards the de-referencing of the "WinCertificate" pointer.
> >>>>>>>>>>>> It does not guard the calculation of hte pointer itself:
> >>>>>>>>>>>>
> >>>>>>>>>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>>>>>>
> >>>>>>>>>>>> This is wrong; if we don't know for sure that we have enough
> room
> >>>>>>>>>>>> for a WIN_CERTIFICATE, then even creating such a pointer, not
> >>>>>>>>>>>> just de-referencing it, may invoke undefined behavior.
> >>>>>>>>>>>>
> >>>>>>>>>>>> Move the pointer calculation after the size check.
> >>>>>>>>>>>>
> >>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>>>> ---
> >>>>>>>>>>>>
> >>>>>>>>>>>>
> >> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.c |
> >>>>>>>> 8
> >>>>>>>>>> +++++---
> >>>>>>>>>>>>  1 file changed, 5 insertions(+), 3 deletions(-)
> >>>>>>>>>>>>
> >>>>>>>>>>>> diff --git
> >>>>>>>>>>
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.c
> >>>>>>>>>>
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.c
> >>>>>>>>>>>> index 8761980c88aa..461ed7cfb5ac 100644
> >>>>>>>>>>>> ---
> >>>>>>>>
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
> >>>>>>>> .c
> >>>>>>>>>>>> +++
> >>>>>>>>>>
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.c
> >>>>>>>>>>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>>>>>>>>>>         OffSet < SecDataDirEnd;
> >>>>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >>>>>>>>>>>> (WinCertificate-
> >>>>>>>>>>> dwLength))) {
> >>>>>>>>>>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
> OffSet);
> >>>>>>>>>>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
> >>>>>>>>>>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
> >>>>>>>>>>>> -        SecDataDirLeft < WinCertificate->dwLength) {
> >>>>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
> >>>>>>>>>>>> +      break;
> >>>>>>>>>>>> +    }
> >>>>>>>>>>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
> OffSet);
> >>>>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
> >>>>>>>>>>>>        break;
> >>>>>>>>>>>>      }
> >>>>>>>>>>>>
> >>>>>>>>>>>> --
> >>>>>>>>>>>> 2.19.1.3.g30247aa5d201
> >>>>>>>>>>>>
> >>>>>>>>>>>
> >>>>>>>>>>> Patch#3:
> >>>>>>>>>>>
> >>>>>>>>>>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17
> >>>> 00:00:00
> >>>>>>>>>> 2001
> >>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
> >>>>>>>>>>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch
> >>>>>>>> alignment
> >>>>>>>>>>>>  overflow (CVE-2019-14562)
> >>>>>>>>>>>>
> >>>>>>>>>>>> The DxeImageVerificationHandler() function currently checks
> >>>>>>>>>>>> whether "SecDataDir" has enough room for
> >>>>>>>>>>>> "WinCertificate->dwLength". However,
> >>>>>>>>>> for
> >>>>>>>>>>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to the
> >>>>>>>>>>>> next multiple of 8. If "WinCertificate->dwLength" is large
> >>>>>>>>>>>> enough, the alignment will return 0, and "OffSet" will be stuck at
> >> the
> >>>> same value.
> >>>>>>>>>>>>
> >>>>>>>>>>>> Check whether "SecDataDir" has room left for both
> >>>>>>>>>>>> "WinCertificate->dwLength" and the alignment.
> >>>>>>>>>>>>
> >>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>>>> ---
> >>>>>>>>>>>>
> >>>>>>>>>>>>
> >> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.c |
> >>>>>>>> 4
> >>>>>>>>>> +++-
> >>>>>>>>>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
> >>>>>>>>>>>>
> >>>>>>>>>>>> diff --git
> >>>>>>>>>>
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.c
> >>>>>>>>>>
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.c
> >>>>>>>>>>>> index 461ed7cfb5ac..e38eb981b7a0 100644
> >>>>>>>>>>>> ---
> >>>>>>>>
> >> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
> >>>>>>>> .c
> >>>>>>>>>>>> +++
> >>>>>>>>>>
> >> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>> ib.c
> >>>>>>>>>>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>        break;
> >>>>>>>>>>>>      }
> >>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>>>>>>>>>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
> >>>>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
> >>>>>>>>>>>> +        (SecDataDirLeft - WinCertificate->dwLength <
> >>>>>>>>>>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
> >>>>>>>>>>>>        break;
> >>>>>>>>>>>>      }
> >>>>>>>>>>>>
> >>>>>>>>>>>> --
> >>>>>>>>>>>> 2.19.1.3.g30247aa5d201
> >>>>>>>>>>>>
> >>>>>>>>>>>
> >>>>>>>>>>> If Wenyi and the reviewers are OK with these patches, I can
> submit
> >>>>>>>>>>> them as a standalone patch series.
> >>>>>>>>>>>
> >>>>>>>>>>> Note that I do not have any reproducer for the issue; the best
> >>>>>>>>>>> testing that I could offer would be some light-weight Secure Boot
> >>>>>>>>>>> regression tests.
> >>>>>>>>>>>
> >>>>>>>>>>> Thanks
> >>>>>>>>>>> Laszlo
> >>>>>>>>>>>
> >>>>>>>>>>>
> >>>>>>>>>>> .
> >>>>>>>>>>>
> >>>>>>>>>>
> >>>>>>>>>>
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>
> >>>>>>>>
> >>>>>>>>
> >>>>>>>
> >>>>>>
> >>>>>
> >>>>>
> >>>>> .
> >>>>>
> >>>>
> >>>>
> >>>>
> >>>
> >>
> >>
> >>
> >
> 
> 
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-31 16:06                             ` Yao, Jiewen
@ 2020-09-01  7:10                               ` wenyi,xie
  2020-09-01  7:31                                 ` Yao, Jiewen
  2020-09-01  7:29                               ` Laszlo Ersek
  1 sibling, 1 reply; 32+ messages in thread
From: wenyi,xie @ 2020-09-01  7:10 UTC (permalink / raw)
  To: Yao, Jiewen, devel@edk2.groups.io, Laszlo Ersek, Wang, Jian J
  Cc: songdongkuang@huawei.com, Mathews, John

I think Laszlo's patches is OK, I have applied and tested it using my case. It can catch the issue.
DEBUG code and log below,

  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
  DEBUG((DEBUG_INFO, "SecDataDirEnd=0x%x.\n", SecDataDirEnd));
  for (OffSet = SecDataDir->VirtualAddress;
       OffSet < SecDataDirEnd;
       OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) {
    SecDataDirLeft = SecDataDirEnd - OffSet;
    DEBUG((DEBUG_INFO, "OffSet=0x%x, SecDataDirLeft=0x%x.\n", OffSet, SecDataDirLeft));
    if (SecDataDirLeft <= sizeof(WIN_CERTIFICATE)) {
      break;
    }
    WinCertificate = (WIN_CERTIFICATE *)(mImageBase + OffSet);
    DEBUG((DEBUG_INFO, "WinCertificate->dwLength=0x%x, ALIGN_SIZE (WinCertificate->dwLength)=0x%x.\n", WinCertificate->dwLength, ALIGN_SIZE(WinCertificate->dwLength)));
    if (SecDataDirLeft < WinCertificate->dwLength ||
	(SecDataDirLeft - WinCertificate->dwLength < ALIGN_SIZE(WinCertificate->dwLength))) {
      DEBUG((DEBUG_INFO, "Parameter is invalid and break.\n"));
      break;
    }

SecDataDirEnd=0xFFFFFFFC.
OffSet=0xE000, SecDataDirLeft=0xFFFF1FFC.
WinCertificate->dwLength=0xFFFF1FFB, ALIGN_SIZE (WinCertificate->dwLength)=0x5.
Parameter is invalid and break.
The image doesn't pass verification: VenHw(5CF32E0B-8EDF-2E44-9CDA-93205E99EC1C,00000000)/VenHw(964E5B22-6459-11D2-8E39-00A0C969723B,00000000)/\signed_1234_4G.efi

Regards
Wenyi

On 2020/9/1 0:06, Yao, Jiewen wrote:
> Sounds great. Appreciate your hard work on that.
> 
> Will you post a patch to fix the issue again?
> 
> Thank you
> Yao Jiewen
> 
>> -----Original Message-----
>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of wenyi,xie
>> via groups.io
>> Sent: Monday, August 31, 2020 7:24 PM
>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Laszlo Ersek
>> <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>
>> Cc: songdongkuang@huawei.com; Mathews, John <john.mathews@intel.com>
>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>
>> Hi,Jiewen,
>>
>> I modify the PE file again, this time it can pass the check in PeCoffLib and cause
>> offset overflow.
>>
>> First, create a PE file and sign it(only one signature), then using binary edit tool
>> to modify content of PE file like below,
>>  1.check the value of SecDataDir->VirtualAddress, in my PE file, it's 0xE000
>>  2.changing SecDataDir->Size from 0x5F8 to 0xFFFF1FFC
>>  3.changing WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFB
>>  4.padding PE file with 0 until the size of the file is 0xFFFFFFFC(it will make the PE
>> file so large)
>> OffSet + WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength) is
>> 0xE000 + 0xFFFF1FFB + 0x5 = 0x100000000
>>
>> Below is the DEBUG code and log, in second loop the offset overflow and
>> become 0
>>
>> for (OffSet = SecDataDir->VirtualAddress;
>>      OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>      OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>> dwLength))) {
>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>   DEBUG((DEBUG_INFO, "OffSet=0x%x.\n", OffSet));
>>   if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>> (WIN_CERTIFICATE) ||
>>       (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
>>> dwLength) {
>>     break;
>>   }
>>   DEBUG((DEBUG_INFO, "WinCertificate->dwLength=0x%x, ALIGN_SIZE
>> (WinCertificate->dwLength)=0x%x.\n", WinCertificate->dwLength,
>> ALIGN_SIZE(WinCertificate->dwLength)));
>>
>>
>> SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFC.
>> OffSet=0xE000.
>> WinCertificate->dwLength=0xFFFF1FFB, ALIGN_SIZE (WinCertificate-
>>> dwLength)=0x5.
>> DxeImageVerificationLib: Image is signed but signature is not allowed by DB and
>> SHA256 hash of image is notOffSet=0x0.
>> WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate-
>>> dwLength)=0x3.
>> OffSet=0x5A50.
>> WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate-
>>> dwLength)=0x4.
>> OffSet=0xEA78.
>> WinCertificate->dwLength=0x0, ALIGN_SIZE (WinCertificate->dwLength)=0x0.
>> The image doesn't pass verification: VenHw(5CF32E0B-8EDF-2E44-9CDA-
>> 93205E99EC1C,00000000)/VenHw(964E5B22-6459-11D2-8E39-
>> 00A0C969723B,00000000)/\signed_1234_4G.efi
>>
>>
>> Regards
>> Wenyi
>>
>>
>> On 2020/8/28 14:43, Yao, Jiewen wrote:
>>> Apology that I did not say clearly.
>>> I mean you should not modify any code to perform an attack.
>>>
>>> I am not asking you to exploit the system. Attack here just means: to cause
>> system hang or buffer overflow. That is enough.
>>> But you cannot modify code to remove any existing checker.
>>>
>>> Thank you
>>> Yao Jiewen
>>>
>>>> -----Original Message-----
>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>> wenyi,xie
>>>> via groups.io
>>>> Sent: Friday, August 28, 2020 2:18 PM
>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Laszlo
>> Ersek
>>>> <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>
>>>> Cc: songdongkuang@huawei.com; Mathews, John
>> <john.mathews@intel.com>
>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>
>>>> Hi,Jiewen,
>>>>
>>>> I don't really get the meaning "create a case that bypass all checks in
>> PeCoffLib",
>>>> do you mean I need to create a PE file that can bypass all check in PeCoffLib
>>>> without modify any
>>>> code and then cause the problem in DxeImageVerificationLib, or just modify
>>>> some code in PeCoffLib to bypass check instead of removing the calling of
>>>> PeCoffLoaderGetImageInfo. Would
>>>> you mind explaining a little more specifically? As far as I tried, it's really hard
>> to
>>>> reproduce the issue without touching any code.
>>>>
>>>> Thanks
>>>> Wenyi
>>>>
>>>> On 2020/8/28 11:50, Yao, Jiewen wrote:
>>>>> HI Wenyi
>>>>> Thank you very much to take time to reproduce.
>>>>>
>>>>> I am particular interested in below:
>>>>> 	"As PE file is modified, function PeCoffLoaderGetImageInfo will return
>>>> error, so I have to remove it so that for loop can be tested in
>>>> DxeImageVerificationHandler."
>>>>>
>>>>> By design, the PeCoffLib should catch illegal PE/COFF image and return error.
>>>> (even it cannot catch all, it should catch most ones).
>>>>> Other PE/COFF parser may rely on the checker in PeCoffLib and *no need*
>> to
>>>> duplicate all checkers.
>>>>> As such, DxeImageVerificationLib (and other PeCoff consumer) just need
>>>> checks what has passed the check in PeCoffLib.
>>>>>
>>>>> I don’t think you should remove the checker. If people can remove the
>> checker,
>>>> I am sure the rest code will be vulnerable, according to the current design.
>>>>> Could you please to create a case that bypass all checks in PeCoffLib, then
>> run
>>>> into DxeImageVerificationLib and cause the problem? That would be more
>>>> valuable for us.
>>>>>
>>>>> Thank you
>>>>> Yao Jiewen
>>>>>
>>>>>> -----Original Message-----
>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>>>> wenyi,xie
>>>>>> via groups.io
>>>>>> Sent: Friday, August 28, 2020 11:18 AM
>>>>>> To: Laszlo Ersek <lersek@redhat.com>; Wang, Jian J
>>>> <jian.j.wang@intel.com>;
>>>>>> devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>
>>>>>> Cc: songdongkuang@huawei.com; Mathews, John
>>>> <john.mathews@intel.com>
>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>
>>>>>> Hi,Laszlo and everyone,
>>>>>>
>>>>>> These days I tried to reproduce the issue,and made some progress. I
>> think
>>>>>> there are two way to cause overflow from a mathematical point of view,
>>>>>> 1.As Laszlo analysed before, if WinCertificate->dwLength is large enough,
>>>> close
>>>>>> to MAX_UINT32, then (WinCertificate->dwLength + ALIGN_SIZE
>>>> (WinCertificate-
>>>>>>> dwLength)) will cause overflow.
>>>>>> 2.(WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength)) is
>>>> good,
>>>>>> OffSet is good, but OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>> (WinCertificate->dwLength)) cause overflow.
>>>>>>
>>>>>> Here I choose the second way to reproduce the issue, I choose a PE file
>> and
>>>> sign
>>>>>> it with my own db certificate. Then I use binary edit tool to modify the PE
>> file
>>>> like
>>>>>> below,
>>>>>>
>>>>>> 1.change SecDataDir->Size from 0x5F8 to 0xFFFF1FFF
>>>>>> 2.change WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFE
>>>>>> SecDataDir->VirtualAddress in my PE is 0xe000 and no need to change.
>>>>>>
>>>>>> As PE file is modified, function PeCoffLoaderGetImageInfo will return error,
>>>> so I
>>>>>> have to remove it so that for loop can be tested in
>>>> DxeImageVerificationHandler.
>>>>>> The log is as below,
>>>>>>
>>>>>> SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFF.
>>>>>> (First Loop)
>>>>>> OffSet=0xE000.
>>>>>> WinCertificate->dwLength=0xFFFF1FFE, ALIGN_SIZE (WinCertificate-
>>>>>>> dwLength)=0x2.
>>>>>> (Second Loop)
>>>>>> OffSet=0x0.
>>>>>> WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate-
>>>>>>> dwLength)=0x3.
>>>>>> (Third Loop)
>>>>>> OffSet=0x5A50.
>>>>>> WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate-
>>>>>>> dwLength)=0x4.
>>>>>> (Forth Loop)
>>>>>> OffSet=0xEA78.
>>>>>> WinCertificate->dwLength=0xAFAFAFAF, ALIGN_SIZE (WinCertificate-
>>>>>>> dwLength)=0x1.
>>>>>> (Fifth Loop)
>>>>>> OffSet=0xAFB09A28.
>>>>>>
>>>>>> As I modify SecDataDir->Size and WinCertificate->dwLength, so in first
>> loop
>>>> the
>>>>>> condition check whether the Security Data Directory has enough room left
>>>> for
>>>>>> "WinCertificate->dwLength" is ok.
>>>>>>
>>>>>> if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>>>>>> (WIN_CERTIFICATE) ||
>>>>>>     (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>> WinCertificate-
>>>>>>> dwLength) {
>>>>>>   break;
>>>>>> }
>>>>>>
>>>>>> In the beginning of second loop, WinCertificate->dwLength + ALIGN_SIZE
>>>>>> (WinCertificate->dwLength) is 0xFFFF2000, offset is 0xE000
>>>>>>
>>>>>> OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>>>> dwLength))
>>>>>>
>>>>>> Offset now is 0 and overflow happens. So even if my PE only have one
>>>> signature,
>>>>>> the for loop is still going untill exception happens.
>>>>>>
>>>>>>
>>>>>> I can't reproduce the issue using the first way, because if WinCertificate-
>>>>>>> dwLength is close to MAX_UINT32, it means SecDataDir->Size should also
>>>> close
>>>>>> to MAX_UINT32, or the condition check
>>>>>> "(SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>> WinCertificate-
>>>>>>> dwLength" will break. But if SecDataDir->Size is very large, SecDataDir-
>>>>>>> VirtualAddress have to be smaller than 8 bytes,
>>>>>> because SecDataDir->VirtualAddress + SecDataDir->Size < MAX_UINT32.
>>>>>> SecDataDir->VirtualAddress is the beginning address of the signature, and
>>>> before
>>>>>> SecDataDir->VirtualAddress is the content of origin PE file, I think it's
>>>> impossible
>>>>>> that the size of PE file is only 8 bytes.
>>>>>>
>>>>>> As I changed the one line code in DxeImageVerificationHandler to
>> reproduce
>>>> the
>>>>>> issue, I'm not sure whether it's ok.
>>>>>>
>>>>>> Thanks
>>>>>> Wenyi
>>>>>>
>>>>>> On 2020/8/19 17:26, Laszlo Ersek wrote:
>>>>>>> On 08/18/20 17:18, Mathews, John wrote:
>>>>>>>> I dug up the original report details.  This was noted as a concern during a
>>>>>> source code inspection.  There was no demonstration of how it might be
>>>>>> triggered.
>>>>>>>>
>>>>>>>> " There is an integer overflow vulnerability in the
>>>>>> DxeImageVerificationHandler function when
>>>>>>>> parsing the PE files attribute certificate table. In cases where
>>>> WinCertificate-
>>>>>>> dwLength is
>>>>>>>> sufficiently large, it's possible to overflow Offset back to 0 causing an
>>>> endless
>>>>>> loop."
>>>>>>>>
>>>>>>>> The recommendation was to add stricter checking of "Offset" and the
>>>>>> embedded length fields of certificate data
>>>>>>>> before using them.
>>>>>>>
>>>>>>> Thanks for checking!
>>>>>>>
>>>>>>> Laszlo
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> -----Original Message-----
>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>> Sent: Tuesday, August 18, 2020 1:59 AM
>>>>>>>> To: Wang, Jian J <jian.j.wang@intel.com>; devel@edk2.groups.io; Yao,
>>>>>> Jiewen <jiewen.yao@intel.com>; xiewenyi2@huawei.com
>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com;
>> Mathews,
>>>>>> John <john.mathews@intel.com>
>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>>>
>>>>>>>> On 08/18/20 04:10, Wang, Jian J wrote:
>>>>>>>>> Laszlo,
>>>>>>>>>
>>>>>>>>> My apologies for the slow response. I'm not the original reporter but
>>>>>>>>> just the BZ submitter. And I didn't do deep analysis to this issue.
>>>>>>>>> The issues was reported from one internal team. Add John in loop to
>> see
>>>> if
>>>>>> he knows more about it or not.
>>>>>>>>>
>>>>>>>>> My superficial understanding on such issue is that, if there's
>>>>>>>>> "potential" issue in theory and hard to reproduce, it's still worthy
>>>>>>>>> of using an alternative way to replace the original implementation
>>>>>>>>> with no "potential" issue at all. Maybe we don't have to prove old way
>> is
>>>>>> something wrong but must prove that the new way is really safe.
>>>>>>>>
>>>>>>>> I agree, thanks.
>>>>>>>>
>>>>>>>> It would be nice to hear more from the internal team about the
>> originally
>>>>>> reported (even if hard-to-trigger) issue.
>>>>>>>>
>>>>>>>> Thanks!
>>>>>>>> Laszlo
>>>>>>>>
>>>>>>>>>
>>>>>>>>> Regards,
>>>>>>>>> Jian
>>>>>>>>>
>>>>>>>>>> -----Original Message-----
>>>>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>>>> Laszlo
>>>>>>>>>> Ersek
>>>>>>>>>> Sent: Tuesday, August 18, 2020 12:53 AM
>>>>>>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
>>>>>>>>>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
>>>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>>>>>
>>>>>>>>>> Hi Jiewen,
>>>>>>>>>>
>>>>>>>>>> On 08/14/20 10:53, Yao, Jiewen wrote:
>>>>>>>>>>>> To Jiewen,
>>>>>>>>>>>> Sorry, I don't have environment to reproduce the issue.
>>>>>>>>>>>
>>>>>>>>>>> Please help me understand, if you don’t have environment to
>>>>>>>>>>> reproduce the
>>>>>>>>>> issue, how do you guarantee that your patch does fix the problem and
>>>>>>>>>> we don’t have any other vulnerabilities?
>>>>>>>>>>
>>>>>>>>>> The original bug report in
>>>>>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is
>> seriously
>>>>>>>>>> lacking. It does not go into detail about the alleged integer overflow.
>>>>>>>>>> It does not quote the code, does not explain the control flow, does
>>>>>>>>>> not identify the exact edk2 commit at which the vulnerability exists.
>>>>>>>>>>
>>>>>>>>>> The bug report also does not offer a reproducer.
>>>>>>>>>>
>>>>>>>>>> Additionally, the exact statement that the bug report does make,
>>>>>>>>>> namely
>>>>>>>>>>
>>>>>>>>>>   it's possible to overflow Offset back to 0 causing an endless loop
>>>>>>>>>>
>>>>>>>>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can
>>>>>>>>>> be overflowed to zero, but the *addend* that is added to OffSet can
>>>>>>>>>> be overflowed to zero. Therefore the infinite loop will arise because
>>>>>>>>>> OffSet remains stuck at its present value, and not because OffSet
>>>>>>>>>> will be re-set to zero.
>>>>>>>>>>
>>>>>>>>>> For the reasons, we can only speculate as to what the actual problem
>>>>>>>>>> is, unless Jian decides to join the discussion and clarifies what he
>>>>>>>>>> had in mind originally.
>>>>>>>>>>
>>>>>>>>>> My understanding (or even "reconstruction") of the vulnerability is
>>>>>>>>>> described above, and in the patches that I proposed.
>>>>>>>>>>
>>>>>>>>>> We can write a patch based on code analysis. It's possible to
>>>>>>>>>> identify integer overflows based on code analysis, and it's possible
>>>>>>>>>> to verify the correctness of fixes by code review. Obviously testing
>>>>>>>>>> is always good, but many times, constructing reproducers for such
>>>>>>>>>> issues that were found by code review, is difficult and time
>>>>>>>>>> consuming. We can say that we don't fix vulnerabilities without
>>>>>>>>>> reproducers, or we can say that we make an effort to fix them even if
>>>>>>>>>> all we have is code analysis (and not a reproducer).
>>>>>>>>>>
>>>>>>>>>> So the above paragraph concerns "correctness". Regarding
>>>>>>>>>> "completeness", I guarantee you that this patch does not fix *all*
>>>>>>>>>> problems related to PE parsing. (See the other BZ tickets.) It does
>>>>>>>>>> fix *one* issue with PE parsing. We can say that we try to fix such
>>>>>>>>>> issues gradually (give different CVE numbers to different issues, and
>>>>>>>>>> address them one at a time), or we can say that we rewrite PE parsing
>>>>>> from the ground up.
>>>>>>>>>> (BTW: I have seriously attempted that in the past, and I gave up,
>>>>>>>>>> because the PE format is FUBAR.)
>>>>>>>>>>
>>>>>>>>>> In summary:
>>>>>>>>>>
>>>>>>>>>> - the problem statement is unclear,
>>>>>>>>>>
>>>>>>>>>> - it seems like there is indeed an integer overflow problem in the
>>>>>>>>>> SecDataDir parsing loop, but it's uncertain whether the bug reporter
>>>>>>>>>> had exactly that in mind
>>>>>>>>>>
>>>>>>>>>> - PE parsing is guaranteed to have other vulnerabilities elsewhere in
>>>>>>>>>> edk2, but I'm currently unaware of other such issues in
>>>>>>>>>> DxeImageVerificationLib specifically
>>>>>>>>>>
>>>>>>>>>> - even if there are other such problems (in DxeImageVerificationLib
>>>>>>>>>> or elswehere), fixing this bug that we know about is likely
>>>>>>>>>> worthwhile
>>>>>>>>>>
>>>>>>>>>> - for many such bugs, constructing a reproducer is difficult and time
>>>>>>>>>> consuming; code analysis, and *regression-testing* are frequently the
>>>>>>>>>> only tools we have. That doesn't mean we should ignore this class of
>>>> bugs.
>>>>>>>>>>
>>>>>>>>>> (Fixing integer overflows retro-actively is more difficult than
>>>>>>>>>> writing overflow-free code in the first place, but that ship has
>>>>>>>>>> sailed; so we can only fight these bugs incrementally now, unless we
>>>>>>>>>> can rewrite PE parsing with a new data structure from the ground up.
>>>>>>>>>> Again I tried that and gave up, because the spec is not public, and
>>>>>>>>>> what I did manage to learn about PE, showed that it was insanely
>>>>>>>>>> over-engineered. I'm not saying that other binary / executable
>>>>>>>>>> formats are better, of course.)
>>>>>>>>>>
>>>>>>>>>> Please check out my patches (inlined elsewhere in this thread), and
>>>>>>>>>> comment whether you'd like me to post them to the list as a
>>>>>>>>>> standalone series.
>>>>>>>>>>
>>>>>>>>>> Jian: it wouldn't hurt if you commented as well.
>>>>>>>>>>
>>>>>>>>>> Thanks
>>>>>>>>>> Laszlo
>>>>>>>>>>
>>>>>>>>>>>> -----Original Message-----
>>>>>>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf
>> Of
>>>>>>>>>> wenyi,xie
>>>>>>>>>>>> via groups.io
>>>>>>>>>>>> Sent: Friday, August 14, 2020 3:54 PM
>>>>>>>>>>>> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io; Yao,
>>>>>>>>>>>> Jiewen <jiewen.yao@intel.com>; Wang, Jian J
>>>> <jian.j.wang@intel.com>
>>>>>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>>>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of
>> Offset
>>>>>>>>>>>>
>>>>>>>>>>>> To Laszlo,
>>>>>>>>>>>> Thank you for your detailed description, I agree with what you
>>>>>>>>>>>> analyzed and
>>>>>>>>>> I'm
>>>>>>>>>>>> OK with your patches, it's
>>>>>>>>>>>> correct and much simpler.
>>>>>>>>>>>>
>>>>>>>>>>>> To Jiewen,
>>>>>>>>>>>> Sorry, I don't have environment to reproduce the issue.
>>>>>>>>>>>>
>>>>>>>>>>>> Thanks
>>>>>>>>>>>> Wenyi
>>>>>>>>>>>>
>>>>>>>>>>>> On 2020/8/14 2:50, Laszlo Ersek wrote:
>>>>>>>>>>>>> On 08/13/20 13:55, Wenyi Xie wrote:
>>>>>>>>>>>>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> There is an integer overflow vulnerability in
>>>>>>>>>>>>>> DxeImageVerificationHandler function when parsing the PE files
>>>>>>>>>>>>>> attribute certificate table. In cases where
>>>>>>>>>>>>>> WinCertificate->dwLength is sufficiently large, it's possible to
>>>>>> overflow Offset back to 0 causing an endless loop.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Check offset inbetween VirtualAddress and VirtualAddress + Size.
>>>>>>>>>>>>>> Using SafeintLib to do offset addition with result check.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>>>>>>>>>>>>>> Cc: Jian J Wang <jian.j.wang@intel.com>
>>>>>>>>>>>>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.inf
>>>>>>>>>> |
>>>>>>>>>>>> 1 +
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.h
>>>>>>>>>> |
>>>>>>>>>>>> 1 +
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c
>>>>>>>>>> |
>>>>>>>>>>>> 111 +++++++++++---------
>>>>>>>>>>>>>>  3 files changed, 63 insertions(+), 50 deletions(-)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.inf
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.inf
>>>>>>>>>>>>>> index 1e1a639857e0..a7ac4830b3d4 100644
>>>>>>>>>>>>>> ---
>>>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.inf
>>>>>>>>>>>>>> +++
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.inf
>>>>>>>>>>>>>> @@ -53,6 +53,7 @@ [LibraryClasses]
>>>>>>>>>>>>>>    SecurityManagementLib
>>>>>>>>>>>>>>    PeCoffLib
>>>>>>>>>>>>>>    TpmMeasurementLib
>>>>>>>>>>>>>> +  SafeIntLib
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>  [Protocols]
>>>>>>>>>>>>>>    gEfiFirmwareVolume2ProtocolGuid       ##
>>>> SOMETIMES_CONSUMES
>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.h
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.h
>>>>>>>>>>>>>> index 17955ff9774c..060273917d5d 100644
>>>>>>>>>>>>>> ---
>>>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.h
>>>>>>>>>>>>>> +++
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.h
>>>>>>>>>>>>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-
>> Patent
>>>>>>>>>>>>>> #include <Library/DevicePathLib.h>  #include
>>>>>>>>>>>>>> <Library/SecurityManagementLib.h>  #include
>> <Library/PeCoffLib.h>
>>>>>>>>>>>>>> +#include <Library/SafeIntLib.h>
>>>>>>>>>>>>>>  #include <Protocol/FirmwareVolume2.h>  #include
>>>>>>>>>>>>>> <Protocol/DevicePath.h>  #include <Protocol/BlockIo.h> diff --
>> git
>>>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>> index 36b87e16d53d..dbc03e28c05b 100644
>>>>>>>>>>>>>> ---
>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>>>> .c
>>>>>>>>>>>>>> +++
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>    EFI_STATUS                           HashStatus;
>>>>>>>>>>>>>>    EFI_STATUS                           DbStatus;
>>>>>>>>>>>>>>    BOOLEAN                              IsFound;
>>>>>>>>>>>>>> +  UINT32                               AlignedLength;
>>>>>>>>>>>>>> +  UINT32                               Result;
>>>>>>>>>>>>>> +  EFI_STATUS                           AddStatus;
>>>>>>>>>>>>>> +  BOOLEAN                              IsAuthDataAssigned;
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>    SignatureList     = NULL;
>>>>>>>>>>>>>>    SignatureListSize = 0;
>>>>>>>>>>>>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
>>>>>>>>>>>>>>    IsVerified        = FALSE;
>>>>>>>>>>>>>>    IsFound           = FALSE;
>>>>>>>>>>>>>> +  Result            = 0;
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>    //
>>>>>>>>>>>>>>    // Check the image type and get policy setting.
>>>>>>>>>>>>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>    // The first certificate starts at offset
>>>>>>>>>>>>>> (SecDataDir->VirtualAddress) from
>>>>>>>>>> the
>>>>>>>>>>>> start of the file.
>>>>>>>>>>>>>>    //
>>>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>>>>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>> (WinCertificate-
>>>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet <
>>>>>>>>>>>>>> + (SecDataDir-
>>>>>>>>>>>>> VirtualAddress + SecDataDir->Size));) {
>>>>>>>>>>>>>> +    IsAuthDataAssigned = FALSE;
>>>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>>>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>> (WinCertificate-
>>>>>>>>>>>>> dwLength);
>>>>>>>>>>>>>
>>>>>>>>>>>>> I disagree with this patch.
>>>>>>>>>>>>>
>>>>>>>>>>>>> The primary reason for my disagreement is that the bug report
>>>>>>>>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is
>>>>>>>>>>>>> inexact, and so this patch tries to fix the wrong thing.
>>>>>>>>>>>>>
>>>>>>>>>>>>> With edk2 master at commit 65904cdbb33c, it is *not* possible to
>>>>>>>>>>>>> overflow the OffSet variable to zero with "WinCertificate-
>>>>> dwLength"
>>>>>>>>>>>>> *purely*, and cause an endless loop. Note that we have (at
>> commit
>>>>>>>>>>>>> 65904cdbb33c):
>>>>>>>>>>>>>
>>>>>>>>>>>>>   for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>>>>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>>>>> (WinCertificate-
>>>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>>>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>>>>>> <= sizeof
>>>>>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>>>>>>>> WinCertificate-
>>>>>>>>>>>>> dwLength) {
>>>>>>>>>>>>>       break;
>>>>>>>>>>>>>     }
>>>>>>>>>>>>>
>>>>>>>>>>>>> The last sub-condition checks whether the Security Data Directory
>>>>>>>>>>>>> has enough room left for "WinCertificate->dwLength". If not, then
>>>>>>>>>>>>> we break out of the loop.
>>>>>>>>>>>>>
>>>>>>>>>>>>> If we *do* have enough room, that is:
>>>>>>>>>>>>>
>>>>>>>>>>>>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >=
>>>>>>>>>> WinCertificate-
>>>>>>>>>>>>> dwLength
>>>>>>>>>>>>>
>>>>>>>>>>>>> then we have (by adding OffSet to both sides):
>>>>>>>>>>>>>
>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet +
>>>>>>>>>>>>> WinCertificate- dwLength
>>>>>>>>>>>>>
>>>>>>>>>>>>> The left hand side is a known-good UINT32, and so incrementing
>>>>>>>>>>>>> OffSet (a
>>>>>>>>>>>>> UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32)
>>>>>>>>>>>>> does not cause an overflow.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Instead, the problem is with the alignment. The "if" statement
>>>>>>>>>>>>> checks whether we have enough room for "dwLength", but then
>>>>>>>>>>>>> "OffSet" is advanced by "dwLength" *aligned up* to the next
>>>>>>>>>>>>> multiple of 8. And that may indeed cause various overflows.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Now, the main problem with the present patch is that it does not
>>>>>>>>>>>>> fix one of those overflows. Namely, consider that "dwLength" is
>>>>>>>>>>>>> very close to
>>>>>>>>>>>>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then
>> aligning
>>>>>>>>>>>>> it up to the next multiple of 8 will yield 0. In other words,
>>>>>> "AlignedLength"
>>>>>>>>>>>>> will be zero.
>>>>>>>>>>>>>
>>>>>>>>>>>>> And when that happens, there's going to be an infinite loop just
>>>>>>>>>>>>> the
>>>>>>>>>>>>> same: "OffSet" will not be zero, but it will be *stuck*. The
>>>>>>>>>>>>> SafeUint32Add() call at the bottom will succeed, but it will not
>>>>>>>>>>>>> change the value of "OffSet".
>>>>>>>>>>>>>
>>>>>>>>>>>>> More at the bottom.
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>>>>>>> <= sizeof
>>>>>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>>>>>>> <
>>>>>>>>>>>> WinCertificate->dwLength) {
>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>        AuthData   = PkcsCertData->CertData;
>>>>>>>>>>>>>>        AuthDataSize = PkcsCertData->Hdr.dwLength -
>>>>>>>>>>>>>> sizeof(PkcsCertData-
>>>>>>>>>>> Hdr);
>>>>>>>>>>>>>> +      IsAuthDataAssigned = TRUE;
>>>>>>>>>>>>>> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>>>>>>>>>      } else if (WinCertificate->wCertificateType ==
>>>>>>>>>> WIN_CERT_TYPE_EFI_GUID)
>>>>>>>>>>>> {
>>>>>>>>>>>>>>        //
>>>>>>>>>>>>>>        // The certificate is formatted as
>>>>>>>>>>>>>> WIN_CERTIFICATE_UEFI_GUID which
>>>>>>>>>> is
>>>>>>>>>>>> described in UEFI Spec.
>>>>>>>>>>>>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>        if (WinCertUefiGuid->Hdr.dwLength <=
>>>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
>>>>>>>>>>>>>>          break;
>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType,
>>>>>> &gEfiCertPkcs7Guid))
>>>>>>>>>> {
>>>>>>>>>>>>>> -        continue;
>>>>>>>>>>>>>> +      if (CompareGuid (&WinCertUefiGuid->CertType,
>>>>>>>>>>>>>> + &gEfiCertPkcs7Guid))
>>>>>>>>>> {
>>>>>>>>>>>>>> +        AuthData = WinCertUefiGuid->CertData;
>>>>>>>>>>>>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>>>>>>>>> +        IsAuthDataAssigned = TRUE;
>>>>>>>>>>>>>> +        HashStatus = HashPeImageByType (AuthData,
>> AuthDataSize);
>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>> -      AuthData = WinCertUefiGuid->CertData;
>>>>>>>>>>>>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>>>>>>>>>      } else {
>>>>>>>>>>>>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
>>>>>>>>>>>>>>          break;
>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>> -      continue;
>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>>>>>>>>> -    if (EFI_ERROR (HashStatus)) {
>>>>>>>>>>>>>> -      continue;
>>>>>>>>>>>>>> -    }
>>>>>>>>>>>>>> -
>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>> -    // Check the digital signature against the revoked certificate
>> in
>>>>>>>>>> forbidden
>>>>>>>>>>>> database (dbx).
>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>>>>>>>>>> -      IsVerified = FALSE;
>>>>>>>>>>>>>> -      break;
>>>>>>>>>>>>>> -    }
>>>>>>>>>>>>>> -
>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>> -    // Check the digital signature against the valid certificate in
>>>>>> allowed
>>>>>>>>>>>> database (db).
>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>> -    if (!IsVerified) {
>>>>>>>>>>>>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>>>>>>>>>> -        IsVerified = TRUE;
>>>>>>>>>>>>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>> +      // Check the digital signature against the revoked
>>>>>>>>>>>>>> + certificate in
>>>>>>>>>> forbidden
>>>>>>>>>>>> database (dbx).
>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>>>>>>>>>> +        IsVerified = FALSE;
>>>>>>>>>>>>>> +        break;
>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>> -    }
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>> -    // Check the image's hash value.
>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>> -    DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>>>>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>>>>>>>>> -                 mImageDigest,
>>>>>>>>>>>>>> -                 &mCertType,
>>>>>>>>>>>>>> -                 mImageDigestSize,
>>>>>>>>>>>>>> -                 &IsFound
>>>>>>>>>>>>>> -                 );
>>>>>>>>>>>>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>>>>>>>>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
>>>>>> signed
>>>>>>>>>> but %s
>>>>>>>>>>>> hash of image is found in DBX.\n", mHashTypeStr));
>>>>>>>>>>>>>> -      IsVerified = FALSE;
>>>>>>>>>>>>>> -      break;
>>>>>>>>>>>>>> -    }
>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>> +      // Check the digital signature against the valid
>>>>>>>>>>>>>> + certificate in allowed
>>>>>>>>>>>> database (db).
>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>> +      if (!IsVerified) {
>>>>>>>>>>>>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>>>>>>>>>> +          IsVerified = TRUE;
>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>> +      }
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> -    if (!IsVerified) {
>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>> +      // Check the image's hash value.
>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>>        DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>>>>>> -                   EFI_IMAGE_SECURITY_DATABASE,
>>>>>>>>>>>>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>>>>>>>>>                     mImageDigest,
>>>>>>>>>>>>>>                     &mCertType,
>>>>>>>>>>>>>>                     mImageDigestSize,
>>>>>>>>>>>>>>                     &IsFound
>>>>>>>>>>>>>>                     );
>>>>>>>>>>>>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>>>>>>>>>> -        IsVerified = TRUE;
>>>>>>>>>>>>>> -      } else {
>>>>>>>>>>>>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
>>>>>> signed
>>>>>>>>>> but
>>>>>>>>>>>> signature is not allowed by DB and %s hash of image is not found in
>>>>>>>>>> DB/DBX.\n",
>>>>>>>>>>>> mHashTypeStr));
>>>>>>>>>>>>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>>>>>>>>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
>>>>>>>>>>>>>> + signed
>>>>>>>>>>>> but %s hash of image is found in DBX.\n", mHashTypeStr));
>>>>>>>>>>>>>> +        IsVerified = FALSE;
>>>>>>>>>>>>>> +        break;
>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +      if (!IsVerified) {
>>>>>>>>>>>>>> +        DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>>>>>> +                     EFI_IMAGE_SECURITY_DATABASE,
>>>>>>>>>>>>>> +                     mImageDigest,
>>>>>>>>>>>>>> +                     &mCertType,
>>>>>>>>>>>>>> +                     mImageDigestSize,
>>>>>>>>>>>>>> +                     &IsFound
>>>>>>>>>>>>>> +                     );
>>>>>>>>>>>>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>>>>>>>>>> +          IsVerified = TRUE;
>>>>>>>>>>>>>> +        } else {
>>>>>>>>>>>>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image
>> is
>>>>>>>>>>>>>> + signed
>>>>>>>>>> but
>>>>>>>>>>>> signature is not allowed by DB and %s hash of image is not found in
>>>>>>>>>> DB/DBX.\n",
>>>>>>>>>>>> mHashTypeStr));
>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>> +      }
>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
>>>>>>>>>>>>>> +    if (EFI_ERROR (AddStatus)) {
>>>>>>>>>>>>>> +      break;
>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>> +    OffSet = Result;
>>>>>>>>>>>>>>    }
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
>>>>>>>>>>>>>> {
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> There are other (smaller) reasons why I dislike this patch:
>>>>>>>>>>>>>
>>>>>>>>>>>>> - The "IsAuthDataAssigned" variable is superfluous; we could use
>>>>>>>>>>>>> the existent "AuthData" variable (with a NULL-check and a
>>>>>>>>>>>>> NULL-assignment) similarly.
>>>>>>>>>>>>>
>>>>>>>>>>>>> - The patch complicates / reorganizes the control flow needlessly.
>>>>>>>>>>>>> This complication originates from placing the checked "OffSet"
>>>>>>>>>>>>> increment at the bottom of the loop, which then requires the
>>>>>>>>>>>>> removal of all the "continue" statements. But we don't need to
>>>>>>>>>>>>> check-and-increment at the bottom. We can keep the increment
>>>>>>>>>>>>> inside the "for" statement, only extend the *existent* room check
>>>>>>>>>>>>> (which I've quoted) to take the alignment into account as well. If
>>>>>>>>>>>>> there is enough room for the alignment in the security data
>>>>>>>>>>>>> directory, then that guarantees there won't be a UINT32 overflow
>>>>>> either.
>>>>>>>>>>>>>
>>>>>>>>>>>>> All in all, I'm proposing the following three patches instead. The
>>>>>>>>>>>>> first two patches are preparation, the last patch is the fix.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Patch#1:
>>>>>>>>>>>>>
>>>>>>>>>>>>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep
>> 17
>>>>>>>>>> 00:00:00
>>>>>>>>>>>> 2001
>>>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
>>>>>>>>>>>>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib:
>> extract
>>>>>>>>>>>>>> SecDataDirEnd, SecDataDirLeft
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> The following two quantities:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> are used multiple times in DxeImageVerificationHandler().
>>>>>>>>>>>>>> Introduce helper variables for them: "SecDataDirEnd" and
>>>>>> "SecDataDirLeft", respectively.
>>>>>>>>>>>>>> This saves us multiple calculations and significantly simplifies the
>>>> code.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Note that all three summands above have type UINT32,
>> therefore
>>>>>>>>>>>>>> the new variables are also of type UINT32.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> This patch does not change behavior.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> (Note that the code already handles the case when the
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> UINT32 addition overflows -- namely, in that case, the
>>>>>>>>>>>>>> certificate loop is never entered, and the corruption check right
>>>>>>>>>>>>>> after the loop fires.)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c |
>>>>>>>>>> 12
>>>>>>>>>>>> ++++++++----
>>>>>>>>>>>>>>  1 file changed, 8 insertions(+), 4 deletions(-)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>> index 36b87e16d53d..8761980c88aa 100644
>>>>>>>>>>>>>> ---
>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>>>> .c
>>>>>>>>>>>>>> +++
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>    UINT8                                *AuthData;
>>>>>>>>>>>>>>    UINTN                                AuthDataSize;
>>>>>>>>>>>>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
>>>>>>>>>>>>>> +  UINT32                               SecDataDirEnd;
>>>>>>>>>>>>>> +  UINT32                               SecDataDirLeft;
>>>>>>>>>>>>>>    UINT32                               OffSet;
>>>>>>>>>>>>>>    CHAR16                               *NameStr;
>>>>>>>>>>>>>>    RETURN_STATUS                        PeCoffStatus;
>>>>>>>>>>>>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>    // "Attribute Certificate Table".
>>>>>>>>>>>>>>    // The first certificate starts at offset
>>>>>>>>>>>>>> (SecDataDir->VirtualAddress) from
>>>>>>>>>> the
>>>>>>>>>>>> start of the file.
>>>>>>>>>>>>>>    //
>>>>>>>>>>>>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir-
>>> Size;
>>>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>>>>>> +       OffSet < SecDataDirEnd;
>>>>>>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>>>>>> (WinCertificate-
>>>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>>>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <=
>>>>>> sizeof
>>>>>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>>>>>>>>>> WinCertificate->dwLength) {
>>>>>>>>>>>>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>> +        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>    }
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
>>>>>>>>>>>>>> {
>>>>>>>>>>>>>> +  if (OffSet != SecDataDirEnd) {
>>>>>>>>>>>>>>      //
>>>>>>>>>>>>>>      // The Size in Certificate Table or the attribute
>>>>>>>>>>>>>> certificate table is
>>>>>>>>>> corrupted.
>>>>>>>>>>>>>>      //
>>>>>>>>>>>>>> --
>>>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> Patch#2:
>>>>>>>>>>>>>
>>>>>>>>>>>>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep
>> 17
>>>>>> 00:00:00
>>>>>>>>>>>> 2001
>>>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
>>>>>>>>>>>>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib:
>> assign
>>>>>>>>>>>>>> WinCertificate after size check
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE))
>> check
>>>>>>>>>>>>>> only guards the de-referencing of the "WinCertificate" pointer.
>>>>>>>>>>>>>> It does not guard the calculation of hte pointer itself:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> This is wrong; if we don't know for sure that we have enough
>> room
>>>>>>>>>>>>>> for a WIN_CERTIFICATE, then even creating such a pointer, not
>>>>>>>>>>>>>> just de-referencing it, may invoke undefined behavior.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Move the pointer calculation after the size check.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c |
>>>>>>>>>> 8
>>>>>>>>>>>> +++++---
>>>>>>>>>>>>>>  1 file changed, 5 insertions(+), 3 deletions(-)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>> index 8761980c88aa..461ed7cfb5ac 100644
>>>>>>>>>>>>>> ---
>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>>>> .c
>>>>>>>>>>>>>> +++
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>>>>         OffSet < SecDataDirEnd;
>>>>>>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>>>>>> (WinCertificate-
>>>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>> OffSet);
>>>>>>>>>>>>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>>>>>>>>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>> -        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
>>>>>>>>>>>>>> +      break;
>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>> OffSet);
>>>>>>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> --
>>>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> Patch#3:
>>>>>>>>>>>>>
>>>>>>>>>>>>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17
>>>>>> 00:00:00
>>>>>>>>>>>> 2001
>>>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
>>>>>>>>>>>>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch
>>>>>>>>>> alignment
>>>>>>>>>>>>>>  overflow (CVE-2019-14562)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> The DxeImageVerificationHandler() function currently checks
>>>>>>>>>>>>>> whether "SecDataDir" has enough room for
>>>>>>>>>>>>>> "WinCertificate->dwLength". However,
>>>>>>>>>>>> for
>>>>>>>>>>>>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to the
>>>>>>>>>>>>>> next multiple of 8. If "WinCertificate->dwLength" is large
>>>>>>>>>>>>>> enough, the alignment will return 0, and "OffSet" will be stuck at
>>>> the
>>>>>> same value.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Check whether "SecDataDir" has room left for both
>>>>>>>>>>>>>> "WinCertificate->dwLength" and the alignment.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c |
>>>>>>>>>> 4
>>>>>>>>>>>> +++-
>>>>>>>>>>>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>> index 461ed7cfb5ac..e38eb981b7a0 100644
>>>>>>>>>>>>>> ---
>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>>>> .c
>>>>>>>>>>>>>> +++
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>>>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
>>>>>>>>>>>>>> +        (SecDataDirLeft - WinCertificate->dwLength <
>>>>>>>>>>>>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> --
>>>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> If Wenyi and the reviewers are OK with these patches, I can
>> submit
>>>>>>>>>>>>> them as a standalone patch series.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Note that I do not have any reproducer for the issue; the best
>>>>>>>>>>>>> testing that I could offer would be some light-weight Secure Boot
>>>>>>>>>>>>> regression tests.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Thanks
>>>>>>>>>>>>> Laszlo
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> .
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> .
>>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>
>>>>
>>>>
>>>
>>
>>
>> 
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-08-31 16:06                             ` Yao, Jiewen
  2020-09-01  7:10                               ` wenyi,xie
@ 2020-09-01  7:29                               ` Laszlo Ersek
  1 sibling, 0 replies; 32+ messages in thread
From: Laszlo Ersek @ 2020-09-01  7:29 UTC (permalink / raw)
  To: devel, jiewen.yao, xiewenyi2@huawei.com, Wang, Jian J
  Cc: songdongkuang@huawei.com, Mathews, John

On 08/31/20 18:06, Yao, Jiewen wrote:
> Sounds great. Appreciate your hard work on that.
> 
> Will you post a patch to fix the issue again?

Should I post the three patches that I had counter-proposed as a
standalone series?

See them at the end of the following message, in-line:

https://edk2.groups.io/g/devel/message/64243
http://mid.mail-archive.com/eb0c6bcb-77fb-2fb9-783e-aa5025953a80@redhat.com

Wenyi, if you were OK with my patches, then I'd ask you to test them
against your reproducer (if / when I post the patches).

Thanks,
Laszlo

>> -----Original Message-----
>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of wenyi,xie
>> via groups.io
>> Sent: Monday, August 31, 2020 7:24 PM
>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Laszlo Ersek
>> <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>
>> Cc: songdongkuang@huawei.com; Mathews, John <john.mathews@intel.com>
>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>
>> Hi,Jiewen,
>>
>> I modify the PE file again, this time it can pass the check in PeCoffLib and cause
>> offset overflow.
>>
>> First, create a PE file and sign it(only one signature), then using binary edit tool
>> to modify content of PE file like below,
>>  1.check the value of SecDataDir->VirtualAddress, in my PE file, it's 0xE000
>>  2.changing SecDataDir->Size from 0x5F8 to 0xFFFF1FFC
>>  3.changing WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFB
>>  4.padding PE file with 0 until the size of the file is 0xFFFFFFFC(it will make the PE
>> file so large)
>> OffSet + WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength) is
>> 0xE000 + 0xFFFF1FFB + 0x5 = 0x100000000
>>
>> Below is the DEBUG code and log, in second loop the offset overflow and
>> become 0
>>
>> for (OffSet = SecDataDir->VirtualAddress;
>>      OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>      OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>> dwLength))) {
>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>   DEBUG((DEBUG_INFO, "OffSet=0x%x.\n", OffSet));
>>   if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>> (WIN_CERTIFICATE) ||
>>       (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
>>> dwLength) {
>>     break;
>>   }
>>   DEBUG((DEBUG_INFO, "WinCertificate->dwLength=0x%x, ALIGN_SIZE
>> (WinCertificate->dwLength)=0x%x.\n", WinCertificate->dwLength,
>> ALIGN_SIZE(WinCertificate->dwLength)));
>>
>>
>> SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFC.
>> OffSet=0xE000.
>> WinCertificate->dwLength=0xFFFF1FFB, ALIGN_SIZE (WinCertificate-
>>> dwLength)=0x5.
>> DxeImageVerificationLib: Image is signed but signature is not allowed by DB and
>> SHA256 hash of image is notOffSet=0x0.
>> WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate-
>>> dwLength)=0x3.
>> OffSet=0x5A50.
>> WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate-
>>> dwLength)=0x4.
>> OffSet=0xEA78.
>> WinCertificate->dwLength=0x0, ALIGN_SIZE (WinCertificate->dwLength)=0x0.
>> The image doesn't pass verification: VenHw(5CF32E0B-8EDF-2E44-9CDA-
>> 93205E99EC1C,00000000)/VenHw(964E5B22-6459-11D2-8E39-
>> 00A0C969723B,00000000)/\signed_1234_4G.efi
>>
>>
>> Regards
>> Wenyi
>>
>>
>> On 2020/8/28 14:43, Yao, Jiewen wrote:
>>> Apology that I did not say clearly.
>>> I mean you should not modify any code to perform an attack.
>>>
>>> I am not asking you to exploit the system. Attack here just means: to cause
>> system hang or buffer overflow. That is enough.
>>> But you cannot modify code to remove any existing checker.
>>>
>>> Thank you
>>> Yao Jiewen
>>>
>>>> -----Original Message-----
>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>> wenyi,xie
>>>> via groups.io
>>>> Sent: Friday, August 28, 2020 2:18 PM
>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Laszlo
>> Ersek
>>>> <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>
>>>> Cc: songdongkuang@huawei.com; Mathews, John
>> <john.mathews@intel.com>
>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>
>>>> Hi,Jiewen,
>>>>
>>>> I don't really get the meaning "create a case that bypass all checks in
>> PeCoffLib",
>>>> do you mean I need to create a PE file that can bypass all check in PeCoffLib
>>>> without modify any
>>>> code and then cause the problem in DxeImageVerificationLib, or just modify
>>>> some code in PeCoffLib to bypass check instead of removing the calling of
>>>> PeCoffLoaderGetImageInfo. Would
>>>> you mind explaining a little more specifically? As far as I tried, it's really hard
>> to
>>>> reproduce the issue without touching any code.
>>>>
>>>> Thanks
>>>> Wenyi
>>>>
>>>> On 2020/8/28 11:50, Yao, Jiewen wrote:
>>>>> HI Wenyi
>>>>> Thank you very much to take time to reproduce.
>>>>>
>>>>> I am particular interested in below:
>>>>> 	"As PE file is modified, function PeCoffLoaderGetImageInfo will return
>>>> error, so I have to remove it so that for loop can be tested in
>>>> DxeImageVerificationHandler."
>>>>>
>>>>> By design, the PeCoffLib should catch illegal PE/COFF image and return error.
>>>> (even it cannot catch all, it should catch most ones).
>>>>> Other PE/COFF parser may rely on the checker in PeCoffLib and *no need*
>> to
>>>> duplicate all checkers.
>>>>> As such, DxeImageVerificationLib (and other PeCoff consumer) just need
>>>> checks what has passed the check in PeCoffLib.
>>>>>
>>>>> I don’t think you should remove the checker. If people can remove the
>> checker,
>>>> I am sure the rest code will be vulnerable, according to the current design.
>>>>> Could you please to create a case that bypass all checks in PeCoffLib, then
>> run
>>>> into DxeImageVerificationLib and cause the problem? That would be more
>>>> valuable for us.
>>>>>
>>>>> Thank you
>>>>> Yao Jiewen
>>>>>
>>>>>> -----Original Message-----
>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>>>> wenyi,xie
>>>>>> via groups.io
>>>>>> Sent: Friday, August 28, 2020 11:18 AM
>>>>>> To: Laszlo Ersek <lersek@redhat.com>; Wang, Jian J
>>>> <jian.j.wang@intel.com>;
>>>>>> devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>
>>>>>> Cc: songdongkuang@huawei.com; Mathews, John
>>>> <john.mathews@intel.com>
>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>
>>>>>> Hi,Laszlo and everyone,
>>>>>>
>>>>>> These days I tried to reproduce the issue,and made some progress. I
>> think
>>>>>> there are two way to cause overflow from a mathematical point of view,
>>>>>> 1.As Laszlo analysed before, if WinCertificate->dwLength is large enough,
>>>> close
>>>>>> to MAX_UINT32, then (WinCertificate->dwLength + ALIGN_SIZE
>>>> (WinCertificate-
>>>>>>> dwLength)) will cause overflow.
>>>>>> 2.(WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength)) is
>>>> good,
>>>>>> OffSet is good, but OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>> (WinCertificate->dwLength)) cause overflow.
>>>>>>
>>>>>> Here I choose the second way to reproduce the issue, I choose a PE file
>> and
>>>> sign
>>>>>> it with my own db certificate. Then I use binary edit tool to modify the PE
>> file
>>>> like
>>>>>> below,
>>>>>>
>>>>>> 1.change SecDataDir->Size from 0x5F8 to 0xFFFF1FFF
>>>>>> 2.change WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFE
>>>>>> SecDataDir->VirtualAddress in my PE is 0xe000 and no need to change.
>>>>>>
>>>>>> As PE file is modified, function PeCoffLoaderGetImageInfo will return error,
>>>> so I
>>>>>> have to remove it so that for loop can be tested in
>>>> DxeImageVerificationHandler.
>>>>>> The log is as below,
>>>>>>
>>>>>> SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFF.
>>>>>> (First Loop)
>>>>>> OffSet=0xE000.
>>>>>> WinCertificate->dwLength=0xFFFF1FFE, ALIGN_SIZE (WinCertificate-
>>>>>>> dwLength)=0x2.
>>>>>> (Second Loop)
>>>>>> OffSet=0x0.
>>>>>> WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate-
>>>>>>> dwLength)=0x3.
>>>>>> (Third Loop)
>>>>>> OffSet=0x5A50.
>>>>>> WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate-
>>>>>>> dwLength)=0x4.
>>>>>> (Forth Loop)
>>>>>> OffSet=0xEA78.
>>>>>> WinCertificate->dwLength=0xAFAFAFAF, ALIGN_SIZE (WinCertificate-
>>>>>>> dwLength)=0x1.
>>>>>> (Fifth Loop)
>>>>>> OffSet=0xAFB09A28.
>>>>>>
>>>>>> As I modify SecDataDir->Size and WinCertificate->dwLength, so in first
>> loop
>>>> the
>>>>>> condition check whether the Security Data Directory has enough room left
>>>> for
>>>>>> "WinCertificate->dwLength" is ok.
>>>>>>
>>>>>> if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>>>>>> (WIN_CERTIFICATE) ||
>>>>>>     (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>> WinCertificate-
>>>>>>> dwLength) {
>>>>>>   break;
>>>>>> }
>>>>>>
>>>>>> In the beginning of second loop, WinCertificate->dwLength + ALIGN_SIZE
>>>>>> (WinCertificate->dwLength) is 0xFFFF2000, offset is 0xE000
>>>>>>
>>>>>> OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>>>> dwLength))
>>>>>>
>>>>>> Offset now is 0 and overflow happens. So even if my PE only have one
>>>> signature,
>>>>>> the for loop is still going untill exception happens.
>>>>>>
>>>>>>
>>>>>> I can't reproduce the issue using the first way, because if WinCertificate-
>>>>>>> dwLength is close to MAX_UINT32, it means SecDataDir->Size should also
>>>> close
>>>>>> to MAX_UINT32, or the condition check
>>>>>> "(SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>> WinCertificate-
>>>>>>> dwLength" will break. But if SecDataDir->Size is very large, SecDataDir-
>>>>>>> VirtualAddress have to be smaller than 8 bytes,
>>>>>> because SecDataDir->VirtualAddress + SecDataDir->Size < MAX_UINT32.
>>>>>> SecDataDir->VirtualAddress is the beginning address of the signature, and
>>>> before
>>>>>> SecDataDir->VirtualAddress is the content of origin PE file, I think it's
>>>> impossible
>>>>>> that the size of PE file is only 8 bytes.
>>>>>>
>>>>>> As I changed the one line code in DxeImageVerificationHandler to
>> reproduce
>>>> the
>>>>>> issue, I'm not sure whether it's ok.
>>>>>>
>>>>>> Thanks
>>>>>> Wenyi
>>>>>>
>>>>>> On 2020/8/19 17:26, Laszlo Ersek wrote:
>>>>>>> On 08/18/20 17:18, Mathews, John wrote:
>>>>>>>> I dug up the original report details.  This was noted as a concern during a
>>>>>> source code inspection.  There was no demonstration of how it might be
>>>>>> triggered.
>>>>>>>>
>>>>>>>> " There is an integer overflow vulnerability in the
>>>>>> DxeImageVerificationHandler function when
>>>>>>>> parsing the PE files attribute certificate table. In cases where
>>>> WinCertificate-
>>>>>>> dwLength is
>>>>>>>> sufficiently large, it's possible to overflow Offset back to 0 causing an
>>>> endless
>>>>>> loop."
>>>>>>>>
>>>>>>>> The recommendation was to add stricter checking of "Offset" and the
>>>>>> embedded length fields of certificate data
>>>>>>>> before using them.
>>>>>>>
>>>>>>> Thanks for checking!
>>>>>>>
>>>>>>> Laszlo
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> -----Original Message-----
>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>> Sent: Tuesday, August 18, 2020 1:59 AM
>>>>>>>> To: Wang, Jian J <jian.j.wang@intel.com>; devel@edk2.groups.io; Yao,
>>>>>> Jiewen <jiewen.yao@intel.com>; xiewenyi2@huawei.com
>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com;
>> Mathews,
>>>>>> John <john.mathews@intel.com>
>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>>>
>>>>>>>> On 08/18/20 04:10, Wang, Jian J wrote:
>>>>>>>>> Laszlo,
>>>>>>>>>
>>>>>>>>> My apologies for the slow response. I'm not the original reporter but
>>>>>>>>> just the BZ submitter. And I didn't do deep analysis to this issue.
>>>>>>>>> The issues was reported from one internal team. Add John in loop to
>> see
>>>> if
>>>>>> he knows more about it or not.
>>>>>>>>>
>>>>>>>>> My superficial understanding on such issue is that, if there's
>>>>>>>>> "potential" issue in theory and hard to reproduce, it's still worthy
>>>>>>>>> of using an alternative way to replace the original implementation
>>>>>>>>> with no "potential" issue at all. Maybe we don't have to prove old way
>> is
>>>>>> something wrong but must prove that the new way is really safe.
>>>>>>>>
>>>>>>>> I agree, thanks.
>>>>>>>>
>>>>>>>> It would be nice to hear more from the internal team about the
>> originally
>>>>>> reported (even if hard-to-trigger) issue.
>>>>>>>>
>>>>>>>> Thanks!
>>>>>>>> Laszlo
>>>>>>>>
>>>>>>>>>
>>>>>>>>> Regards,
>>>>>>>>> Jian
>>>>>>>>>
>>>>>>>>>> -----Original Message-----
>>>>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>>>> Laszlo
>>>>>>>>>> Ersek
>>>>>>>>>> Sent: Tuesday, August 18, 2020 12:53 AM
>>>>>>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
>>>>>>>>>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
>>>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>>>>>
>>>>>>>>>> Hi Jiewen,
>>>>>>>>>>
>>>>>>>>>> On 08/14/20 10:53, Yao, Jiewen wrote:
>>>>>>>>>>>> To Jiewen,
>>>>>>>>>>>> Sorry, I don't have environment to reproduce the issue.
>>>>>>>>>>>
>>>>>>>>>>> Please help me understand, if you don’t have environment to
>>>>>>>>>>> reproduce the
>>>>>>>>>> issue, how do you guarantee that your patch does fix the problem and
>>>>>>>>>> we don’t have any other vulnerabilities?
>>>>>>>>>>
>>>>>>>>>> The original bug report in
>>>>>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is
>> seriously
>>>>>>>>>> lacking. It does not go into detail about the alleged integer overflow.
>>>>>>>>>> It does not quote the code, does not explain the control flow, does
>>>>>>>>>> not identify the exact edk2 commit at which the vulnerability exists.
>>>>>>>>>>
>>>>>>>>>> The bug report also does not offer a reproducer.
>>>>>>>>>>
>>>>>>>>>> Additionally, the exact statement that the bug report does make,
>>>>>>>>>> namely
>>>>>>>>>>
>>>>>>>>>>   it's possible to overflow Offset back to 0 causing an endless loop
>>>>>>>>>>
>>>>>>>>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can
>>>>>>>>>> be overflowed to zero, but the *addend* that is added to OffSet can
>>>>>>>>>> be overflowed to zero. Therefore the infinite loop will arise because
>>>>>>>>>> OffSet remains stuck at its present value, and not because OffSet
>>>>>>>>>> will be re-set to zero.
>>>>>>>>>>
>>>>>>>>>> For the reasons, we can only speculate as to what the actual problem
>>>>>>>>>> is, unless Jian decides to join the discussion and clarifies what he
>>>>>>>>>> had in mind originally.
>>>>>>>>>>
>>>>>>>>>> My understanding (or even "reconstruction") of the vulnerability is
>>>>>>>>>> described above, and in the patches that I proposed.
>>>>>>>>>>
>>>>>>>>>> We can write a patch based on code analysis. It's possible to
>>>>>>>>>> identify integer overflows based on code analysis, and it's possible
>>>>>>>>>> to verify the correctness of fixes by code review. Obviously testing
>>>>>>>>>> is always good, but many times, constructing reproducers for such
>>>>>>>>>> issues that were found by code review, is difficult and time
>>>>>>>>>> consuming. We can say that we don't fix vulnerabilities without
>>>>>>>>>> reproducers, or we can say that we make an effort to fix them even if
>>>>>>>>>> all we have is code analysis (and not a reproducer).
>>>>>>>>>>
>>>>>>>>>> So the above paragraph concerns "correctness". Regarding
>>>>>>>>>> "completeness", I guarantee you that this patch does not fix *all*
>>>>>>>>>> problems related to PE parsing. (See the other BZ tickets.) It does
>>>>>>>>>> fix *one* issue with PE parsing. We can say that we try to fix such
>>>>>>>>>> issues gradually (give different CVE numbers to different issues, and
>>>>>>>>>> address them one at a time), or we can say that we rewrite PE parsing
>>>>>> from the ground up.
>>>>>>>>>> (BTW: I have seriously attempted that in the past, and I gave up,
>>>>>>>>>> because the PE format is FUBAR.)
>>>>>>>>>>
>>>>>>>>>> In summary:
>>>>>>>>>>
>>>>>>>>>> - the problem statement is unclear,
>>>>>>>>>>
>>>>>>>>>> - it seems like there is indeed an integer overflow problem in the
>>>>>>>>>> SecDataDir parsing loop, but it's uncertain whether the bug reporter
>>>>>>>>>> had exactly that in mind
>>>>>>>>>>
>>>>>>>>>> - PE parsing is guaranteed to have other vulnerabilities elsewhere in
>>>>>>>>>> edk2, but I'm currently unaware of other such issues in
>>>>>>>>>> DxeImageVerificationLib specifically
>>>>>>>>>>
>>>>>>>>>> - even if there are other such problems (in DxeImageVerificationLib
>>>>>>>>>> or elswehere), fixing this bug that we know about is likely
>>>>>>>>>> worthwhile
>>>>>>>>>>
>>>>>>>>>> - for many such bugs, constructing a reproducer is difficult and time
>>>>>>>>>> consuming; code analysis, and *regression-testing* are frequently the
>>>>>>>>>> only tools we have. That doesn't mean we should ignore this class of
>>>> bugs.
>>>>>>>>>>
>>>>>>>>>> (Fixing integer overflows retro-actively is more difficult than
>>>>>>>>>> writing overflow-free code in the first place, but that ship has
>>>>>>>>>> sailed; so we can only fight these bugs incrementally now, unless we
>>>>>>>>>> can rewrite PE parsing with a new data structure from the ground up.
>>>>>>>>>> Again I tried that and gave up, because the spec is not public, and
>>>>>>>>>> what I did manage to learn about PE, showed that it was insanely
>>>>>>>>>> over-engineered. I'm not saying that other binary / executable
>>>>>>>>>> formats are better, of course.)
>>>>>>>>>>
>>>>>>>>>> Please check out my patches (inlined elsewhere in this thread), and
>>>>>>>>>> comment whether you'd like me to post them to the list as a
>>>>>>>>>> standalone series.
>>>>>>>>>>
>>>>>>>>>> Jian: it wouldn't hurt if you commented as well.
>>>>>>>>>>
>>>>>>>>>> Thanks
>>>>>>>>>> Laszlo
>>>>>>>>>>
>>>>>>>>>>>> -----Original Message-----
>>>>>>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf
>> Of
>>>>>>>>>> wenyi,xie
>>>>>>>>>>>> via groups.io
>>>>>>>>>>>> Sent: Friday, August 14, 2020 3:54 PM
>>>>>>>>>>>> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io; Yao,
>>>>>>>>>>>> Jiewen <jiewen.yao@intel.com>; Wang, Jian J
>>>> <jian.j.wang@intel.com>
>>>>>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>>>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of
>> Offset
>>>>>>>>>>>>
>>>>>>>>>>>> To Laszlo,
>>>>>>>>>>>> Thank you for your detailed description, I agree with what you
>>>>>>>>>>>> analyzed and
>>>>>>>>>> I'm
>>>>>>>>>>>> OK with your patches, it's
>>>>>>>>>>>> correct and much simpler.
>>>>>>>>>>>>
>>>>>>>>>>>> To Jiewen,
>>>>>>>>>>>> Sorry, I don't have environment to reproduce the issue.
>>>>>>>>>>>>
>>>>>>>>>>>> Thanks
>>>>>>>>>>>> Wenyi
>>>>>>>>>>>>
>>>>>>>>>>>> On 2020/8/14 2:50, Laszlo Ersek wrote:
>>>>>>>>>>>>> On 08/13/20 13:55, Wenyi Xie wrote:
>>>>>>>>>>>>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> There is an integer overflow vulnerability in
>>>>>>>>>>>>>> DxeImageVerificationHandler function when parsing the PE files
>>>>>>>>>>>>>> attribute certificate table. In cases where
>>>>>>>>>>>>>> WinCertificate->dwLength is sufficiently large, it's possible to
>>>>>> overflow Offset back to 0 causing an endless loop.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Check offset inbetween VirtualAddress and VirtualAddress + Size.
>>>>>>>>>>>>>> Using SafeintLib to do offset addition with result check.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>>>>>>>>>>>>>> Cc: Jian J Wang <jian.j.wang@intel.com>
>>>>>>>>>>>>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.inf
>>>>>>>>>> |
>>>>>>>>>>>> 1 +
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.h
>>>>>>>>>> |
>>>>>>>>>>>> 1 +
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c
>>>>>>>>>> |
>>>>>>>>>>>> 111 +++++++++++---------
>>>>>>>>>>>>>>  3 files changed, 63 insertions(+), 50 deletions(-)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.inf
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.inf
>>>>>>>>>>>>>> index 1e1a639857e0..a7ac4830b3d4 100644
>>>>>>>>>>>>>> ---
>>>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.inf
>>>>>>>>>>>>>> +++
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.inf
>>>>>>>>>>>>>> @@ -53,6 +53,7 @@ [LibraryClasses]
>>>>>>>>>>>>>>    SecurityManagementLib
>>>>>>>>>>>>>>    PeCoffLib
>>>>>>>>>>>>>>    TpmMeasurementLib
>>>>>>>>>>>>>> +  SafeIntLib
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>  [Protocols]
>>>>>>>>>>>>>>    gEfiFirmwareVolume2ProtocolGuid       ##
>>>> SOMETIMES_CONSUMES
>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.h
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.h
>>>>>>>>>>>>>> index 17955ff9774c..060273917d5d 100644
>>>>>>>>>>>>>> ---
>>>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.h
>>>>>>>>>>>>>> +++
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.h
>>>>>>>>>>>>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-
>> Patent
>>>>>>>>>>>>>> #include <Library/DevicePathLib.h>  #include
>>>>>>>>>>>>>> <Library/SecurityManagementLib.h>  #include
>> <Library/PeCoffLib.h>
>>>>>>>>>>>>>> +#include <Library/SafeIntLib.h>
>>>>>>>>>>>>>>  #include <Protocol/FirmwareVolume2.h>  #include
>>>>>>>>>>>>>> <Protocol/DevicePath.h>  #include <Protocol/BlockIo.h> diff --
>> git
>>>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>> index 36b87e16d53d..dbc03e28c05b 100644
>>>>>>>>>>>>>> ---
>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>>>> .c
>>>>>>>>>>>>>> +++
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>    EFI_STATUS                           HashStatus;
>>>>>>>>>>>>>>    EFI_STATUS                           DbStatus;
>>>>>>>>>>>>>>    BOOLEAN                              IsFound;
>>>>>>>>>>>>>> +  UINT32                               AlignedLength;
>>>>>>>>>>>>>> +  UINT32                               Result;
>>>>>>>>>>>>>> +  EFI_STATUS                           AddStatus;
>>>>>>>>>>>>>> +  BOOLEAN                              IsAuthDataAssigned;
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>    SignatureList     = NULL;
>>>>>>>>>>>>>>    SignatureListSize = 0;
>>>>>>>>>>>>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
>>>>>>>>>>>>>>    IsVerified        = FALSE;
>>>>>>>>>>>>>>    IsFound           = FALSE;
>>>>>>>>>>>>>> +  Result            = 0;
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>    //
>>>>>>>>>>>>>>    // Check the image type and get policy setting.
>>>>>>>>>>>>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>    // The first certificate starts at offset
>>>>>>>>>>>>>> (SecDataDir->VirtualAddress) from
>>>>>>>>>> the
>>>>>>>>>>>> start of the file.
>>>>>>>>>>>>>>    //
>>>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>>>>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>> (WinCertificate-
>>>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet <
>>>>>>>>>>>>>> + (SecDataDir-
>>>>>>>>>>>>> VirtualAddress + SecDataDir->Size));) {
>>>>>>>>>>>>>> +    IsAuthDataAssigned = FALSE;
>>>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>>>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>> (WinCertificate-
>>>>>>>>>>>>> dwLength);
>>>>>>>>>>>>>
>>>>>>>>>>>>> I disagree with this patch.
>>>>>>>>>>>>>
>>>>>>>>>>>>> The primary reason for my disagreement is that the bug report
>>>>>>>>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is
>>>>>>>>>>>>> inexact, and so this patch tries to fix the wrong thing.
>>>>>>>>>>>>>
>>>>>>>>>>>>> With edk2 master at commit 65904cdbb33c, it is *not* possible to
>>>>>>>>>>>>> overflow the OffSet variable to zero with "WinCertificate-
>>>>> dwLength"
>>>>>>>>>>>>> *purely*, and cause an endless loop. Note that we have (at
>> commit
>>>>>>>>>>>>> 65904cdbb33c):
>>>>>>>>>>>>>
>>>>>>>>>>>>>   for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>>>>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>>>>> (WinCertificate-
>>>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>>>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>>>>>> <= sizeof
>>>>>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>>>>>>>> WinCertificate-
>>>>>>>>>>>>> dwLength) {
>>>>>>>>>>>>>       break;
>>>>>>>>>>>>>     }
>>>>>>>>>>>>>
>>>>>>>>>>>>> The last sub-condition checks whether the Security Data Directory
>>>>>>>>>>>>> has enough room left for "WinCertificate->dwLength". If not, then
>>>>>>>>>>>>> we break out of the loop.
>>>>>>>>>>>>>
>>>>>>>>>>>>> If we *do* have enough room, that is:
>>>>>>>>>>>>>
>>>>>>>>>>>>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >=
>>>>>>>>>> WinCertificate-
>>>>>>>>>>>>> dwLength
>>>>>>>>>>>>>
>>>>>>>>>>>>> then we have (by adding OffSet to both sides):
>>>>>>>>>>>>>
>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet +
>>>>>>>>>>>>> WinCertificate- dwLength
>>>>>>>>>>>>>
>>>>>>>>>>>>> The left hand side is a known-good UINT32, and so incrementing
>>>>>>>>>>>>> OffSet (a
>>>>>>>>>>>>> UINT32) *solely* by "WinCertificate->dwLength" (also a UINT32)
>>>>>>>>>>>>> does not cause an overflow.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Instead, the problem is with the alignment. The "if" statement
>>>>>>>>>>>>> checks whether we have enough room for "dwLength", but then
>>>>>>>>>>>>> "OffSet" is advanced by "dwLength" *aligned up* to the next
>>>>>>>>>>>>> multiple of 8. And that may indeed cause various overflows.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Now, the main problem with the present patch is that it does not
>>>>>>>>>>>>> fix one of those overflows. Namely, consider that "dwLength" is
>>>>>>>>>>>>> very close to
>>>>>>>>>>>>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then
>> aligning
>>>>>>>>>>>>> it up to the next multiple of 8 will yield 0. In other words,
>>>>>> "AlignedLength"
>>>>>>>>>>>>> will be zero.
>>>>>>>>>>>>>
>>>>>>>>>>>>> And when that happens, there's going to be an infinite loop just
>>>>>>>>>>>>> the
>>>>>>>>>>>>> same: "OffSet" will not be zero, but it will be *stuck*. The
>>>>>>>>>>>>> SafeUint32Add() call at the bottom will succeed, but it will not
>>>>>>>>>>>>> change the value of "OffSet".
>>>>>>>>>>>>>
>>>>>>>>>>>>> More at the bottom.
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>>>>>>> <= sizeof
>>>>>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>>>>>>> <
>>>>>>>>>>>> WinCertificate->dwLength) {
>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>        AuthData   = PkcsCertData->CertData;
>>>>>>>>>>>>>>        AuthDataSize = PkcsCertData->Hdr.dwLength -
>>>>>>>>>>>>>> sizeof(PkcsCertData-
>>>>>>>>>>> Hdr);
>>>>>>>>>>>>>> +      IsAuthDataAssigned = TRUE;
>>>>>>>>>>>>>> +      HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>>>>>>>>>      } else if (WinCertificate->wCertificateType ==
>>>>>>>>>> WIN_CERT_TYPE_EFI_GUID)
>>>>>>>>>>>> {
>>>>>>>>>>>>>>        //
>>>>>>>>>>>>>>        // The certificate is formatted as
>>>>>>>>>>>>>> WIN_CERTIFICATE_UEFI_GUID which
>>>>>>>>>> is
>>>>>>>>>>>> described in UEFI Spec.
>>>>>>>>>>>>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>        if (WinCertUefiGuid->Hdr.dwLength <=
>>>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
>>>>>>>>>>>>>>          break;
>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType,
>>>>>> &gEfiCertPkcs7Guid))
>>>>>>>>>> {
>>>>>>>>>>>>>> -        continue;
>>>>>>>>>>>>>> +      if (CompareGuid (&WinCertUefiGuid->CertType,
>>>>>>>>>>>>>> + &gEfiCertPkcs7Guid))
>>>>>>>>>> {
>>>>>>>>>>>>>> +        AuthData = WinCertUefiGuid->CertData;
>>>>>>>>>>>>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>>>>>>>>> +        IsAuthDataAssigned = TRUE;
>>>>>>>>>>>>>> +        HashStatus = HashPeImageByType (AuthData,
>> AuthDataSize);
>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>> -      AuthData = WinCertUefiGuid->CertData;
>>>>>>>>>>>>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>>>>>>>>>      } else {
>>>>>>>>>>>>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
>>>>>>>>>>>>>>          break;
>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>> -      continue;
>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> -    HashStatus = HashPeImageByType (AuthData, AuthDataSize);
>>>>>>>>>>>>>> -    if (EFI_ERROR (HashStatus)) {
>>>>>>>>>>>>>> -      continue;
>>>>>>>>>>>>>> -    }
>>>>>>>>>>>>>> -
>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>> -    // Check the digital signature against the revoked certificate
>> in
>>>>>>>>>> forbidden
>>>>>>>>>>>> database (dbx).
>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>>>>>>>>>> -      IsVerified = FALSE;
>>>>>>>>>>>>>> -      break;
>>>>>>>>>>>>>> -    }
>>>>>>>>>>>>>> -
>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>> -    // Check the digital signature against the valid certificate in
>>>>>> allowed
>>>>>>>>>>>> database (db).
>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>> -    if (!IsVerified) {
>>>>>>>>>>>>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>>>>>>>>>> -        IsVerified = TRUE;
>>>>>>>>>>>>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>> +      // Check the digital signature against the revoked
>>>>>>>>>>>>>> + certificate in
>>>>>>>>>> forbidden
>>>>>>>>>>>> database (dbx).
>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>>>>>>>>>> +        IsVerified = FALSE;
>>>>>>>>>>>>>> +        break;
>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>> -    }
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>> -    // Check the image's hash value.
>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>> -    DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>>>>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>>>>>>>>> -                 mImageDigest,
>>>>>>>>>>>>>> -                 &mCertType,
>>>>>>>>>>>>>> -                 mImageDigestSize,
>>>>>>>>>>>>>> -                 &IsFound
>>>>>>>>>>>>>> -                 );
>>>>>>>>>>>>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>>>>>>>>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
>>>>>> signed
>>>>>>>>>> but %s
>>>>>>>>>>>> hash of image is found in DBX.\n", mHashTypeStr));
>>>>>>>>>>>>>> -      IsVerified = FALSE;
>>>>>>>>>>>>>> -      break;
>>>>>>>>>>>>>> -    }
>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>> +      // Check the digital signature against the valid
>>>>>>>>>>>>>> + certificate in allowed
>>>>>>>>>>>> database (db).
>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>> +      if (!IsVerified) {
>>>>>>>>>>>>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>>>>>>>>>> +          IsVerified = TRUE;
>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>> +      }
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> -    if (!IsVerified) {
>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>> +      // Check the image's hash value.
>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>>        DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>>>>>> -                   EFI_IMAGE_SECURITY_DATABASE,
>>>>>>>>>>>>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>>>>>>>>>                     mImageDigest,
>>>>>>>>>>>>>>                     &mCertType,
>>>>>>>>>>>>>>                     mImageDigestSize,
>>>>>>>>>>>>>>                     &IsFound
>>>>>>>>>>>>>>                     );
>>>>>>>>>>>>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>>>>>>>>>> -        IsVerified = TRUE;
>>>>>>>>>>>>>> -      } else {
>>>>>>>>>>>>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
>>>>>> signed
>>>>>>>>>> but
>>>>>>>>>>>> signature is not allowed by DB and %s hash of image is not found in
>>>>>>>>>> DB/DBX.\n",
>>>>>>>>>>>> mHashTypeStr));
>>>>>>>>>>>>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>>>>>>>>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is
>>>>>>>>>>>>>> + signed
>>>>>>>>>>>> but %s hash of image is found in DBX.\n", mHashTypeStr));
>>>>>>>>>>>>>> +        IsVerified = FALSE;
>>>>>>>>>>>>>> +        break;
>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +      if (!IsVerified) {
>>>>>>>>>>>>>> +        DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>>>>>> +                     EFI_IMAGE_SECURITY_DATABASE,
>>>>>>>>>>>>>> +                     mImageDigest,
>>>>>>>>>>>>>> +                     &mCertType,
>>>>>>>>>>>>>> +                     mImageDigestSize,
>>>>>>>>>>>>>> +                     &IsFound
>>>>>>>>>>>>>> +                     );
>>>>>>>>>>>>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>>>>>>>>>> +          IsVerified = TRUE;
>>>>>>>>>>>>>> +        } else {
>>>>>>>>>>>>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image
>> is
>>>>>>>>>>>>>> + signed
>>>>>>>>>> but
>>>>>>>>>>>> signature is not allowed by DB and %s hash of image is not found in
>>>>>>>>>> DB/DBX.\n",
>>>>>>>>>>>> mHashTypeStr));
>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>> +      }
>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength, &Result);
>>>>>>>>>>>>>> +    if (EFI_ERROR (AddStatus)) {
>>>>>>>>>>>>>> +      break;
>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>> +    OffSet = Result;
>>>>>>>>>>>>>>    }
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
>>>>>>>>>>>>>> {
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> There are other (smaller) reasons why I dislike this patch:
>>>>>>>>>>>>>
>>>>>>>>>>>>> - The "IsAuthDataAssigned" variable is superfluous; we could use
>>>>>>>>>>>>> the existent "AuthData" variable (with a NULL-check and a
>>>>>>>>>>>>> NULL-assignment) similarly.
>>>>>>>>>>>>>
>>>>>>>>>>>>> - The patch complicates / reorganizes the control flow needlessly.
>>>>>>>>>>>>> This complication originates from placing the checked "OffSet"
>>>>>>>>>>>>> increment at the bottom of the loop, which then requires the
>>>>>>>>>>>>> removal of all the "continue" statements. But we don't need to
>>>>>>>>>>>>> check-and-increment at the bottom. We can keep the increment
>>>>>>>>>>>>> inside the "for" statement, only extend the *existent* room check
>>>>>>>>>>>>> (which I've quoted) to take the alignment into account as well. If
>>>>>>>>>>>>> there is enough room for the alignment in the security data
>>>>>>>>>>>>> directory, then that guarantees there won't be a UINT32 overflow
>>>>>> either.
>>>>>>>>>>>>>
>>>>>>>>>>>>> All in all, I'm proposing the following three patches instead. The
>>>>>>>>>>>>> first two patches are preparation, the last patch is the fix.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Patch#1:
>>>>>>>>>>>>>
>>>>>>>>>>>>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon Sep
>> 17
>>>>>>>>>> 00:00:00
>>>>>>>>>>>> 2001
>>>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
>>>>>>>>>>>>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib:
>> extract
>>>>>>>>>>>>>> SecDataDirEnd, SecDataDirLeft
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> The following two quantities:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> are used multiple times in DxeImageVerificationHandler().
>>>>>>>>>>>>>> Introduce helper variables for them: "SecDataDirEnd" and
>>>>>> "SecDataDirLeft", respectively.
>>>>>>>>>>>>>> This saves us multiple calculations and significantly simplifies the
>>>> code.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Note that all three summands above have type UINT32,
>> therefore
>>>>>>>>>>>>>> the new variables are also of type UINT32.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> This patch does not change behavior.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> (Note that the code already handles the case when the
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> UINT32 addition overflows -- namely, in that case, the
>>>>>>>>>>>>>> certificate loop is never entered, and the corruption check right
>>>>>>>>>>>>>> after the loop fires.)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c |
>>>>>>>>>> 12
>>>>>>>>>>>> ++++++++----
>>>>>>>>>>>>>>  1 file changed, 8 insertions(+), 4 deletions(-)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>> index 36b87e16d53d..8761980c88aa 100644
>>>>>>>>>>>>>> ---
>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>>>> .c
>>>>>>>>>>>>>> +++
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>    UINT8                                *AuthData;
>>>>>>>>>>>>>>    UINTN                                AuthDataSize;
>>>>>>>>>>>>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
>>>>>>>>>>>>>> +  UINT32                               SecDataDirEnd;
>>>>>>>>>>>>>> +  UINT32                               SecDataDirLeft;
>>>>>>>>>>>>>>    UINT32                               OffSet;
>>>>>>>>>>>>>>    CHAR16                               *NameStr;
>>>>>>>>>>>>>>    RETURN_STATUS                        PeCoffStatus;
>>>>>>>>>>>>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>    // "Attribute Certificate Table".
>>>>>>>>>>>>>>    // The first certificate starts at offset
>>>>>>>>>>>>>> (SecDataDir->VirtualAddress) from
>>>>>>>>>> the
>>>>>>>>>>>> start of the file.
>>>>>>>>>>>>>>    //
>>>>>>>>>>>>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir-
>>> Size;
>>>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>>>>>> +       OffSet < SecDataDirEnd;
>>>>>>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>>>>>> (WinCertificate-
>>>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>>>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <=
>>>>>> sizeof
>>>>>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>>>>>>>>>> WinCertificate->dwLength) {
>>>>>>>>>>>>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>> +        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>    }
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
>>>>>>>>>>>>>> {
>>>>>>>>>>>>>> +  if (OffSet != SecDataDirEnd) {
>>>>>>>>>>>>>>      //
>>>>>>>>>>>>>>      // The Size in Certificate Table or the attribute
>>>>>>>>>>>>>> certificate table is
>>>>>>>>>> corrupted.
>>>>>>>>>>>>>>      //
>>>>>>>>>>>>>> --
>>>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> Patch#2:
>>>>>>>>>>>>>
>>>>>>>>>>>>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep
>> 17
>>>>>> 00:00:00
>>>>>>>>>>>> 2001
>>>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
>>>>>>>>>>>>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib:
>> assign
>>>>>>>>>>>>>> WinCertificate after size check
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE))
>> check
>>>>>>>>>>>>>> only guards the de-referencing of the "WinCertificate" pointer.
>>>>>>>>>>>>>> It does not guard the calculation of hte pointer itself:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> This is wrong; if we don't know for sure that we have enough
>> room
>>>>>>>>>>>>>> for a WIN_CERTIFICATE, then even creating such a pointer, not
>>>>>>>>>>>>>> just de-referencing it, may invoke undefined behavior.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Move the pointer calculation after the size check.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c |
>>>>>>>>>> 8
>>>>>>>>>>>> +++++---
>>>>>>>>>>>>>>  1 file changed, 5 insertions(+), 3 deletions(-)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>> index 8761980c88aa..461ed7cfb5ac 100644
>>>>>>>>>>>>>> ---
>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>>>> .c
>>>>>>>>>>>>>> +++
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>>>>         OffSet < SecDataDirEnd;
>>>>>>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>>>>>> (WinCertificate-
>>>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>> OffSet);
>>>>>>>>>>>>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>>>>>>>>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>> -        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
>>>>>>>>>>>>>> +      break;
>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>> OffSet);
>>>>>>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> --
>>>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> Patch#3:
>>>>>>>>>>>>>
>>>>>>>>>>>>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep 17
>>>>>> 00:00:00
>>>>>>>>>>>> 2001
>>>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
>>>>>>>>>>>>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch
>>>>>>>>>> alignment
>>>>>>>>>>>>>>  overflow (CVE-2019-14562)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> The DxeImageVerificationHandler() function currently checks
>>>>>>>>>>>>>> whether "SecDataDir" has enough room for
>>>>>>>>>>>>>> "WinCertificate->dwLength". However,
>>>>>>>>>>>> for
>>>>>>>>>>>>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to the
>>>>>>>>>>>>>> next multiple of 8. If "WinCertificate->dwLength" is large
>>>>>>>>>>>>>> enough, the alignment will return 0, and "OffSet" will be stuck at
>>>> the
>>>>>> same value.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Check whether "SecDataDir" has room left for both
>>>>>>>>>>>>>> "WinCertificate->dwLength" and the alignment.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c |
>>>>>>>>>> 4
>>>>>>>>>>>> +++-
>>>>>>>>>>>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>> index 461ed7cfb5ac..e38eb981b7a0 100644
>>>>>>>>>>>>>> ---
>>>>>>>>>>
>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>>>> .c
>>>>>>>>>>>>>> +++
>>>>>>>>>>>>
>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>>>>>>>>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
>>>>>>>>>>>>>> +        (SecDataDirLeft - WinCertificate->dwLength <
>>>>>>>>>>>>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> --
>>>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> If Wenyi and the reviewers are OK with these patches, I can
>> submit
>>>>>>>>>>>>> them as a standalone patch series.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Note that I do not have any reproducer for the issue; the best
>>>>>>>>>>>>> testing that I could offer would be some light-weight Secure Boot
>>>>>>>>>>>>> regression tests.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Thanks
>>>>>>>>>>>>> Laszlo
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> .
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> .
>>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>
>>>>
>>>>
>>>
>>
>>
>>
> 
> 
> 
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-09-01  7:10                               ` wenyi,xie
@ 2020-09-01  7:31                                 ` Yao, Jiewen
  2020-09-01  7:43                                   ` wenyi,xie
  0 siblings, 1 reply; 32+ messages in thread
From: Yao, Jiewen @ 2020-09-01  7:31 UTC (permalink / raw)
  To: devel@edk2.groups.io, xiewenyi2@huawei.com, Laszlo Ersek,
	Wang, Jian J
  Cc: songdongkuang@huawei.com, Mathews, John

I am sorry, that I am a little lost here.

We have discussed different patches. I am not 100% sure which one is "Laszlo's patches".

To make thing easy and record all actions, would you please reply the patch(es) you have verified, with your "tested-by:" tag?

Thank you
Yao Jiewen

> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of wenyi,xie
> via groups.io
> Sent: Tuesday, September 1, 2020 3:11 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Laszlo Ersek
> <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>
> Cc: songdongkuang@huawei.com; Mathews, John <john.mathews@intel.com>
> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> 
> I think Laszlo's patches is OK, I have applied and tested it using my case. It can
> catch the issue.
> DEBUG code and log below,
> 
>   SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
>   DEBUG((DEBUG_INFO, "SecDataDirEnd=0x%x.\n", SecDataDirEnd));
>   for (OffSet = SecDataDir->VirtualAddress;
>        OffSet < SecDataDirEnd;
>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >dwLength))) {
>     SecDataDirLeft = SecDataDirEnd - OffSet;
>     DEBUG((DEBUG_INFO, "OffSet=0x%x, SecDataDirLeft=0x%x.\n", OffSet,
> SecDataDirLeft));
>     if (SecDataDirLeft <= sizeof(WIN_CERTIFICATE)) {
>       break;
>     }
>     WinCertificate = (WIN_CERTIFICATE *)(mImageBase + OffSet);
>     DEBUG((DEBUG_INFO, "WinCertificate->dwLength=0x%x, ALIGN_SIZE
> (WinCertificate->dwLength)=0x%x.\n", WinCertificate->dwLength,
> ALIGN_SIZE(WinCertificate->dwLength)));
>     if (SecDataDirLeft < WinCertificate->dwLength ||
> 	(SecDataDirLeft - WinCertificate->dwLength <
> ALIGN_SIZE(WinCertificate->dwLength))) {
>       DEBUG((DEBUG_INFO, "Parameter is invalid and break.\n"));
>       break;
>     }
> 
> SecDataDirEnd=0xFFFFFFFC.
> OffSet=0xE000, SecDataDirLeft=0xFFFF1FFC.
> WinCertificate->dwLength=0xFFFF1FFB, ALIGN_SIZE (WinCertificate-
> >dwLength)=0x5.
> Parameter is invalid and break.
> The image doesn't pass verification: VenHw(5CF32E0B-8EDF-2E44-9CDA-
> 93205E99EC1C,00000000)/VenHw(964E5B22-6459-11D2-8E39-
> 00A0C969723B,00000000)/\signed_1234_4G.efi
> 
> Regards
> Wenyi
> 
> On 2020/9/1 0:06, Yao, Jiewen wrote:
> > Sounds great. Appreciate your hard work on that.
> >
> > Will you post a patch to fix the issue again?
> >
> > Thank you
> > Yao Jiewen
> >
> >> -----Original Message-----
> >> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
> wenyi,xie
> >> via groups.io
> >> Sent: Monday, August 31, 2020 7:24 PM
> >> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Laszlo
> Ersek
> >> <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>
> >> Cc: songdongkuang@huawei.com; Mathews, John
> <john.mathews@intel.com>
> >> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> >>
> >> Hi,Jiewen,
> >>
> >> I modify the PE file again, this time it can pass the check in PeCoffLib and
> cause
> >> offset overflow.
> >>
> >> First, create a PE file and sign it(only one signature), then using binary edit
> tool
> >> to modify content of PE file like below,
> >>  1.check the value of SecDataDir->VirtualAddress, in my PE file, it's 0xE000
> >>  2.changing SecDataDir->Size from 0x5F8 to 0xFFFF1FFC
> >>  3.changing WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFB
> >>  4.padding PE file with 0 until the size of the file is 0xFFFFFFFC(it will make the
> PE
> >> file so large)
> >> OffSet + WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength)
> is
> >> 0xE000 + 0xFFFF1FFB + 0x5 = 0x100000000
> >>
> >> Below is the DEBUG code and log, in second loop the offset overflow and
> >> become 0
> >>
> >> for (OffSet = SecDataDir->VirtualAddress;
> >>      OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>      OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >>> dwLength))) {
> >>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
> >>   DEBUG((DEBUG_INFO, "OffSet=0x%x.\n", OffSet));
> >>   if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
> >> (WIN_CERTIFICATE) ||
> >>       (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
> >>> dwLength) {
> >>     break;
> >>   }
> >>   DEBUG((DEBUG_INFO, "WinCertificate->dwLength=0x%x, ALIGN_SIZE
> >> (WinCertificate->dwLength)=0x%x.\n", WinCertificate->dwLength,
> >> ALIGN_SIZE(WinCertificate->dwLength)));
> >>
> >>
> >> SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFC.
> >> OffSet=0xE000.
> >> WinCertificate->dwLength=0xFFFF1FFB, ALIGN_SIZE (WinCertificate-
> >>> dwLength)=0x5.
> >> DxeImageVerificationLib: Image is signed but signature is not allowed by DB
> and
> >> SHA256 hash of image is notOffSet=0x0.
> >> WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate-
> >>> dwLength)=0x3.
> >> OffSet=0x5A50.
> >> WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate-
> >>> dwLength)=0x4.
> >> OffSet=0xEA78.
> >> WinCertificate->dwLength=0x0, ALIGN_SIZE (WinCertificate->dwLength)=0x0.
> >> The image doesn't pass verification: VenHw(5CF32E0B-8EDF-2E44-9CDA-
> >> 93205E99EC1C,00000000)/VenHw(964E5B22-6459-11D2-8E39-
> >> 00A0C969723B,00000000)/\signed_1234_4G.efi
> >>
> >>
> >> Regards
> >> Wenyi
> >>
> >>
> >> On 2020/8/28 14:43, Yao, Jiewen wrote:
> >>> Apology that I did not say clearly.
> >>> I mean you should not modify any code to perform an attack.
> >>>
> >>> I am not asking you to exploit the system. Attack here just means: to cause
> >> system hang or buffer overflow. That is enough.
> >>> But you cannot modify code to remove any existing checker.
> >>>
> >>> Thank you
> >>> Yao Jiewen
> >>>
> >>>> -----Original Message-----
> >>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
> >> wenyi,xie
> >>>> via groups.io
> >>>> Sent: Friday, August 28, 2020 2:18 PM
> >>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Laszlo
> >> Ersek
> >>>> <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>
> >>>> Cc: songdongkuang@huawei.com; Mathews, John
> >> <john.mathews@intel.com>
> >>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> >>>>
> >>>> Hi,Jiewen,
> >>>>
> >>>> I don't really get the meaning "create a case that bypass all checks in
> >> PeCoffLib",
> >>>> do you mean I need to create a PE file that can bypass all check in
> PeCoffLib
> >>>> without modify any
> >>>> code and then cause the problem in DxeImageVerificationLib, or just
> modify
> >>>> some code in PeCoffLib to bypass check instead of removing the calling of
> >>>> PeCoffLoaderGetImageInfo. Would
> >>>> you mind explaining a little more specifically? As far as I tried, it's really
> hard
> >> to
> >>>> reproduce the issue without touching any code.
> >>>>
> >>>> Thanks
> >>>> Wenyi
> >>>>
> >>>> On 2020/8/28 11:50, Yao, Jiewen wrote:
> >>>>> HI Wenyi
> >>>>> Thank you very much to take time to reproduce.
> >>>>>
> >>>>> I am particular interested in below:
> >>>>> 	"As PE file is modified, function PeCoffLoaderGetImageInfo will return
> >>>> error, so I have to remove it so that for loop can be tested in
> >>>> DxeImageVerificationHandler."
> >>>>>
> >>>>> By design, the PeCoffLib should catch illegal PE/COFF image and return
> error.
> >>>> (even it cannot catch all, it should catch most ones).
> >>>>> Other PE/COFF parser may rely on the checker in PeCoffLib and *no
> need*
> >> to
> >>>> duplicate all checkers.
> >>>>> As such, DxeImageVerificationLib (and other PeCoff consumer) just need
> >>>> checks what has passed the check in PeCoffLib.
> >>>>>
> >>>>> I don’t think you should remove the checker. If people can remove the
> >> checker,
> >>>> I am sure the rest code will be vulnerable, according to the current design.
> >>>>> Could you please to create a case that bypass all checks in PeCoffLib,
> then
> >> run
> >>>> into DxeImageVerificationLib and cause the problem? That would be more
> >>>> valuable for us.
> >>>>>
> >>>>> Thank you
> >>>>> Yao Jiewen
> >>>>>
> >>>>>> -----Original Message-----
> >>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
> >>>> wenyi,xie
> >>>>>> via groups.io
> >>>>>> Sent: Friday, August 28, 2020 11:18 AM
> >>>>>> To: Laszlo Ersek <lersek@redhat.com>; Wang, Jian J
> >>>> <jian.j.wang@intel.com>;
> >>>>>> devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>
> >>>>>> Cc: songdongkuang@huawei.com; Mathews, John
> >>>> <john.mathews@intel.com>
> >>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> >>>>>>
> >>>>>> Hi,Laszlo and everyone,
> >>>>>>
> >>>>>> These days I tried to reproduce the issue,and made some progress. I
> >> think
> >>>>>> there are two way to cause overflow from a mathematical point of view,
> >>>>>> 1.As Laszlo analysed before, if WinCertificate->dwLength is large
> enough,
> >>>> close
> >>>>>> to MAX_UINT32, then (WinCertificate->dwLength + ALIGN_SIZE
> >>>> (WinCertificate-
> >>>>>>> dwLength)) will cause overflow.
> >>>>>> 2.(WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))
> is
> >>>> good,
> >>>>>> OffSet is good, but OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >>>>>> (WinCertificate->dwLength)) cause overflow.
> >>>>>>
> >>>>>> Here I choose the second way to reproduce the issue, I choose a PE file
> >> and
> >>>> sign
> >>>>>> it with my own db certificate. Then I use binary edit tool to modify the
> PE
> >> file
> >>>> like
> >>>>>> below,
> >>>>>>
> >>>>>> 1.change SecDataDir->Size from 0x5F8 to 0xFFFF1FFF
> >>>>>> 2.change WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFE
> >>>>>> SecDataDir->VirtualAddress in my PE is 0xe000 and no need to change.
> >>>>>>
> >>>>>> As PE file is modified, function PeCoffLoaderGetImageInfo will return
> error,
> >>>> so I
> >>>>>> have to remove it so that for loop can be tested in
> >>>> DxeImageVerificationHandler.
> >>>>>> The log is as below,
> >>>>>>
> >>>>>> SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFF.
> >>>>>> (First Loop)
> >>>>>> OffSet=0xE000.
> >>>>>> WinCertificate->dwLength=0xFFFF1FFE, ALIGN_SIZE (WinCertificate-
> >>>>>>> dwLength)=0x2.
> >>>>>> (Second Loop)
> >>>>>> OffSet=0x0.
> >>>>>> WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate-
> >>>>>>> dwLength)=0x3.
> >>>>>> (Third Loop)
> >>>>>> OffSet=0x5A50.
> >>>>>> WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate-
> >>>>>>> dwLength)=0x4.
> >>>>>> (Forth Loop)
> >>>>>> OffSet=0xEA78.
> >>>>>> WinCertificate->dwLength=0xAFAFAFAF, ALIGN_SIZE (WinCertificate-
> >>>>>>> dwLength)=0x1.
> >>>>>> (Fifth Loop)
> >>>>>> OffSet=0xAFB09A28.
> >>>>>>
> >>>>>> As I modify SecDataDir->Size and WinCertificate->dwLength, so in first
> >> loop
> >>>> the
> >>>>>> condition check whether the Security Data Directory has enough room
> left
> >>>> for
> >>>>>> "WinCertificate->dwLength" is ok.
> >>>>>>
> >>>>>> if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
> >>>>>> (WIN_CERTIFICATE) ||
> >>>>>>     (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> >> WinCertificate-
> >>>>>>> dwLength) {
> >>>>>>   break;
> >>>>>> }
> >>>>>>
> >>>>>> In the beginning of second loop, WinCertificate->dwLength +
> ALIGN_SIZE
> >>>>>> (WinCertificate->dwLength) is 0xFFFF2000, offset is 0xE000
> >>>>>>
> >>>>>> OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
> >>>>> dwLength))
> >>>>>>
> >>>>>> Offset now is 0 and overflow happens. So even if my PE only have one
> >>>> signature,
> >>>>>> the for loop is still going untill exception happens.
> >>>>>>
> >>>>>>
> >>>>>> I can't reproduce the issue using the first way, because if WinCertificate-
> >>>>>>> dwLength is close to MAX_UINT32, it means SecDataDir->Size should
> also
> >>>> close
> >>>>>> to MAX_UINT32, or the condition check
> >>>>>> "(SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> >> WinCertificate-
> >>>>>>> dwLength" will break. But if SecDataDir->Size is very large, SecDataDir-
> >>>>>>> VirtualAddress have to be smaller than 8 bytes,
> >>>>>> because SecDataDir->VirtualAddress + SecDataDir->Size < MAX_UINT32.
> >>>>>> SecDataDir->VirtualAddress is the beginning address of the signature,
> and
> >>>> before
> >>>>>> SecDataDir->VirtualAddress is the content of origin PE file, I think it's
> >>>> impossible
> >>>>>> that the size of PE file is only 8 bytes.
> >>>>>>
> >>>>>> As I changed the one line code in DxeImageVerificationHandler to
> >> reproduce
> >>>> the
> >>>>>> issue, I'm not sure whether it's ok.
> >>>>>>
> >>>>>> Thanks
> >>>>>> Wenyi
> >>>>>>
> >>>>>> On 2020/8/19 17:26, Laszlo Ersek wrote:
> >>>>>>> On 08/18/20 17:18, Mathews, John wrote:
> >>>>>>>> I dug up the original report details.  This was noted as a concern
> during a
> >>>>>> source code inspection.  There was no demonstration of how it might be
> >>>>>> triggered.
> >>>>>>>>
> >>>>>>>> " There is an integer overflow vulnerability in the
> >>>>>> DxeImageVerificationHandler function when
> >>>>>>>> parsing the PE files attribute certificate table. In cases where
> >>>> WinCertificate-
> >>>>>>> dwLength is
> >>>>>>>> sufficiently large, it's possible to overflow Offset back to 0 causing an
> >>>> endless
> >>>>>> loop."
> >>>>>>>>
> >>>>>>>> The recommendation was to add stricter checking of "Offset" and the
> >>>>>> embedded length fields of certificate data
> >>>>>>>> before using them.
> >>>>>>>
> >>>>>>> Thanks for checking!
> >>>>>>>
> >>>>>>> Laszlo
> >>>>>>>
> >>>>>>>>
> >>>>>>>>
> >>>>>>>>
> >>>>>>>> -----Original Message-----
> >>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>> Sent: Tuesday, August 18, 2020 1:59 AM
> >>>>>>>> To: Wang, Jian J <jian.j.wang@intel.com>; devel@edk2.groups.io;
> Yao,
> >>>>>> Jiewen <jiewen.yao@intel.com>; xiewenyi2@huawei.com
> >>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com;
> >> Mathews,
> >>>>>> John <john.mathews@intel.com>
> >>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
> >>>>>>>>
> >>>>>>>> On 08/18/20 04:10, Wang, Jian J wrote:
> >>>>>>>>> Laszlo,
> >>>>>>>>>
> >>>>>>>>> My apologies for the slow response. I'm not the original reporter but
> >>>>>>>>> just the BZ submitter. And I didn't do deep analysis to this issue.
> >>>>>>>>> The issues was reported from one internal team. Add John in loop to
> >> see
> >>>> if
> >>>>>> he knows more about it or not.
> >>>>>>>>>
> >>>>>>>>> My superficial understanding on such issue is that, if there's
> >>>>>>>>> "potential" issue in theory and hard to reproduce, it's still worthy
> >>>>>>>>> of using an alternative way to replace the original implementation
> >>>>>>>>> with no "potential" issue at all. Maybe we don't have to prove old
> way
> >> is
> >>>>>> something wrong but must prove that the new way is really safe.
> >>>>>>>>
> >>>>>>>> I agree, thanks.
> >>>>>>>>
> >>>>>>>> It would be nice to hear more from the internal team about the
> >> originally
> >>>>>> reported (even if hard-to-trigger) issue.
> >>>>>>>>
> >>>>>>>> Thanks!
> >>>>>>>> Laszlo
> >>>>>>>>
> >>>>>>>>>
> >>>>>>>>> Regards,
> >>>>>>>>> Jian
> >>>>>>>>>
> >>>>>>>>>> -----Original Message-----
> >>>>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf
> Of
> >>>> Laszlo
> >>>>>>>>>> Ersek
> >>>>>>>>>> Sent: Tuesday, August 18, 2020 12:53 AM
> >>>>>>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
> >>>>>>>>>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
> >>>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
> >>>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >>>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of
> Offset
> >>>>>>>>>>
> >>>>>>>>>> Hi Jiewen,
> >>>>>>>>>>
> >>>>>>>>>> On 08/14/20 10:53, Yao, Jiewen wrote:
> >>>>>>>>>>>> To Jiewen,
> >>>>>>>>>>>> Sorry, I don't have environment to reproduce the issue.
> >>>>>>>>>>>
> >>>>>>>>>>> Please help me understand, if you don’t have environment to
> >>>>>>>>>>> reproduce the
> >>>>>>>>>> issue, how do you guarantee that your patch does fix the problem
> and
> >>>>>>>>>> we don’t have any other vulnerabilities?
> >>>>>>>>>>
> >>>>>>>>>> The original bug report in
> >>>>>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is
> >> seriously
> >>>>>>>>>> lacking. It does not go into detail about the alleged integer
> overflow.
> >>>>>>>>>> It does not quote the code, does not explain the control flow, does
> >>>>>>>>>> not identify the exact edk2 commit at which the vulnerability exists.
> >>>>>>>>>>
> >>>>>>>>>> The bug report also does not offer a reproducer.
> >>>>>>>>>>
> >>>>>>>>>> Additionally, the exact statement that the bug report does make,
> >>>>>>>>>> namely
> >>>>>>>>>>
> >>>>>>>>>>   it's possible to overflow Offset back to 0 causing an endless loop
> >>>>>>>>>>
> >>>>>>>>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can
> >>>>>>>>>> be overflowed to zero, but the *addend* that is added to OffSet
> can
> >>>>>>>>>> be overflowed to zero. Therefore the infinite loop will arise
> because
> >>>>>>>>>> OffSet remains stuck at its present value, and not because OffSet
> >>>>>>>>>> will be re-set to zero.
> >>>>>>>>>>
> >>>>>>>>>> For the reasons, we can only speculate as to what the actual
> problem
> >>>>>>>>>> is, unless Jian decides to join the discussion and clarifies what he
> >>>>>>>>>> had in mind originally.
> >>>>>>>>>>
> >>>>>>>>>> My understanding (or even "reconstruction") of the vulnerability is
> >>>>>>>>>> described above, and in the patches that I proposed.
> >>>>>>>>>>
> >>>>>>>>>> We can write a patch based on code analysis. It's possible to
> >>>>>>>>>> identify integer overflows based on code analysis, and it's possible
> >>>>>>>>>> to verify the correctness of fixes by code review. Obviously testing
> >>>>>>>>>> is always good, but many times, constructing reproducers for such
> >>>>>>>>>> issues that were found by code review, is difficult and time
> >>>>>>>>>> consuming. We can say that we don't fix vulnerabilities without
> >>>>>>>>>> reproducers, or we can say that we make an effort to fix them even
> if
> >>>>>>>>>> all we have is code analysis (and not a reproducer).
> >>>>>>>>>>
> >>>>>>>>>> So the above paragraph concerns "correctness". Regarding
> >>>>>>>>>> "completeness", I guarantee you that this patch does not fix *all*
> >>>>>>>>>> problems related to PE parsing. (See the other BZ tickets.) It does
> >>>>>>>>>> fix *one* issue with PE parsing. We can say that we try to fix such
> >>>>>>>>>> issues gradually (give different CVE numbers to different issues, and
> >>>>>>>>>> address them one at a time), or we can say that we rewrite PE
> parsing
> >>>>>> from the ground up.
> >>>>>>>>>> (BTW: I have seriously attempted that in the past, and I gave up,
> >>>>>>>>>> because the PE format is FUBAR.)
> >>>>>>>>>>
> >>>>>>>>>> In summary:
> >>>>>>>>>>
> >>>>>>>>>> - the problem statement is unclear,
> >>>>>>>>>>
> >>>>>>>>>> - it seems like there is indeed an integer overflow problem in the
> >>>>>>>>>> SecDataDir parsing loop, but it's uncertain whether the bug
> reporter
> >>>>>>>>>> had exactly that in mind
> >>>>>>>>>>
> >>>>>>>>>> - PE parsing is guaranteed to have other vulnerabilities elsewhere in
> >>>>>>>>>> edk2, but I'm currently unaware of other such issues in
> >>>>>>>>>> DxeImageVerificationLib specifically
> >>>>>>>>>>
> >>>>>>>>>> - even if there are other such problems (in DxeImageVerificationLib
> >>>>>>>>>> or elswehere), fixing this bug that we know about is likely
> >>>>>>>>>> worthwhile
> >>>>>>>>>>
> >>>>>>>>>> - for many such bugs, constructing a reproducer is difficult and time
> >>>>>>>>>> consuming; code analysis, and *regression-testing* are frequently
> the
> >>>>>>>>>> only tools we have. That doesn't mean we should ignore this class
> of
> >>>> bugs.
> >>>>>>>>>>
> >>>>>>>>>> (Fixing integer overflows retro-actively is more difficult than
> >>>>>>>>>> writing overflow-free code in the first place, but that ship has
> >>>>>>>>>> sailed; so we can only fight these bugs incrementally now, unless
> we
> >>>>>>>>>> can rewrite PE parsing with a new data structure from the ground
> up.
> >>>>>>>>>> Again I tried that and gave up, because the spec is not public, and
> >>>>>>>>>> what I did manage to learn about PE, showed that it was insanely
> >>>>>>>>>> over-engineered. I'm not saying that other binary / executable
> >>>>>>>>>> formats are better, of course.)
> >>>>>>>>>>
> >>>>>>>>>> Please check out my patches (inlined elsewhere in this thread), and
> >>>>>>>>>> comment whether you'd like me to post them to the list as a
> >>>>>>>>>> standalone series.
> >>>>>>>>>>
> >>>>>>>>>> Jian: it wouldn't hurt if you commented as well.
> >>>>>>>>>>
> >>>>>>>>>> Thanks
> >>>>>>>>>> Laszlo
> >>>>>>>>>>
> >>>>>>>>>>>> -----Original Message-----
> >>>>>>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf
> >> Of
> >>>>>>>>>> wenyi,xie
> >>>>>>>>>>>> via groups.io
> >>>>>>>>>>>> Sent: Friday, August 14, 2020 3:54 PM
> >>>>>>>>>>>> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io;
> Yao,
> >>>>>>>>>>>> Jiewen <jiewen.yao@intel.com>; Wang, Jian J
> >>>> <jian.j.wang@intel.com>
> >>>>>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
> >>>>>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
> >>>>>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of
> >> Offset
> >>>>>>>>>>>>
> >>>>>>>>>>>> To Laszlo,
> >>>>>>>>>>>> Thank you for your detailed description, I agree with what you
> >>>>>>>>>>>> analyzed and
> >>>>>>>>>> I'm
> >>>>>>>>>>>> OK with your patches, it's
> >>>>>>>>>>>> correct and much simpler.
> >>>>>>>>>>>>
> >>>>>>>>>>>> To Jiewen,
> >>>>>>>>>>>> Sorry, I don't have environment to reproduce the issue.
> >>>>>>>>>>>>
> >>>>>>>>>>>> Thanks
> >>>>>>>>>>>> Wenyi
> >>>>>>>>>>>>
> >>>>>>>>>>>> On 2020/8/14 2:50, Laszlo Ersek wrote:
> >>>>>>>>>>>>> On 08/13/20 13:55, Wenyi Xie wrote:
> >>>>>>>>>>>>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> There is an integer overflow vulnerability in
> >>>>>>>>>>>>>> DxeImageVerificationHandler function when parsing the PE
> files
> >>>>>>>>>>>>>> attribute certificate table. In cases where
> >>>>>>>>>>>>>> WinCertificate->dwLength is sufficiently large, it's possible to
> >>>>>> overflow Offset back to 0 causing an endless loop.
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> Check offset inbetween VirtualAddress and VirtualAddress +
> Size.
> >>>>>>>>>>>>>> Using SafeintLib to do offset addition with result check.
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
> >>>>>>>>>>>>>> Cc: Jian J Wang <jian.j.wang@intel.com>
> >>>>>>>>>>>>>> Cc: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>>>>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
> >>>>>>>>>>>>>> ---
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>>
> >>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>>>> ib.inf
> >>>>>>>>>> |
> >>>>>>>>>>>> 1 +
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>>
> >>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>>>> ib.h
> >>>>>>>>>> |
> >>>>>>>>>>>> 1 +
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>>
> >>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>>>> ib.c
> >>>>>>>>>> |
> >>>>>>>>>>>> 111 +++++++++++---------
> >>>>>>>>>>>>>>  3 files changed, 63 insertions(+), 50 deletions(-)
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> diff --git
> >>>>>>>>>>>>
> >>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.inf
> >>>>>>>>>>>>
> >>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.inf
> >>>>>>>>>>>>>> index 1e1a639857e0..a7ac4830b3d4 100644
> >>>>>>>>>>>>>> ---
> >>>>>>>>>>>>
> >>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.inf
> >>>>>>>>>>>>>> +++
> >>>>>>>>>>>>
> >>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.inf
> >>>>>>>>>>>>>> @@ -53,6 +53,7 @@ [LibraryClasses]
> >>>>>>>>>>>>>>    SecurityManagementLib
> >>>>>>>>>>>>>>    PeCoffLib
> >>>>>>>>>>>>>>    TpmMeasurementLib
> >>>>>>>>>>>>>> +  SafeIntLib
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>>  [Protocols]
> >>>>>>>>>>>>>>    gEfiFirmwareVolume2ProtocolGuid       ##
> >>>> SOMETIMES_CONSUMES
> >>>>>>>>>>>>>> diff --git
> >>>>>>>>>>>>
> >>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.h
> >>>>>>>>>>>>
> >>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.h
> >>>>>>>>>>>>>> index 17955ff9774c..060273917d5d 100644
> >>>>>>>>>>>>>> ---
> >>>>>>>>>>>>
> >>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.h
> >>>>>>>>>>>>>> +++
> >>>>>>>>>>>>
> >>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.h
> >>>>>>>>>>>>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-
> >> Patent
> >>>>>>>>>>>>>> #include <Library/DevicePathLib.h>  #include
> >>>>>>>>>>>>>> <Library/SecurityManagementLib.h>  #include
> >> <Library/PeCoffLib.h>
> >>>>>>>>>>>>>> +#include <Library/SafeIntLib.h>
> >>>>>>>>>>>>>>  #include <Protocol/FirmwareVolume2.h>  #include
> >>>>>>>>>>>>>> <Protocol/DevicePath.h>  #include <Protocol/BlockIo.h> diff -
> -
> >> git
> >>>>>>>>>>>>
> >>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.c
> >>>>>>>>>>>>
> >>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.c
> >>>>>>>>>>>>>> index 36b87e16d53d..dbc03e28c05b 100644
> >>>>>>>>>>>>>> ---
> >>>>>>>>>>
> >>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
> >>>>>>>>>> .c
> >>>>>>>>>>>>>> +++
> >>>>>>>>>>>>
> >>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.c
> >>>>>>>>>>>>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>>>    EFI_STATUS                           HashStatus;
> >>>>>>>>>>>>>>    EFI_STATUS                           DbStatus;
> >>>>>>>>>>>>>>    BOOLEAN                              IsFound;
> >>>>>>>>>>>>>> +  UINT32                               AlignedLength;
> >>>>>>>>>>>>>> +  UINT32                               Result;
> >>>>>>>>>>>>>> +  EFI_STATUS                           AddStatus;
> >>>>>>>>>>>>>> +  BOOLEAN                              IsAuthDataAssigned;
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>>    SignatureList     = NULL;
> >>>>>>>>>>>>>>    SignatureListSize = 0;
> >>>>>>>>>>>>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
> >>>>>>>>>>>>>>    IsVerified        = FALSE;
> >>>>>>>>>>>>>>    IsFound           = FALSE;
> >>>>>>>>>>>>>> +  Result            = 0;
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>>    //
> >>>>>>>>>>>>>>    // Check the image type and get policy setting.
> >>>>>>>>>>>>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>>>    // The first certificate starts at offset
> >>>>>>>>>>>>>> (SecDataDir->VirtualAddress) from
> >>>>>>>>>> the
> >>>>>>>>>>>> start of the file.
> >>>>>>>>>>>>>>    //
> >>>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>>>>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>>>>>>>>>>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >>>>>> (WinCertificate-
> >>>>>>>>>>>>> dwLength))) {
> >>>>>>>>>>>>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet <
> >>>>>>>>>>>>>> + (SecDataDir-
> >>>>>>>>>>>>> VirtualAddress + SecDataDir->Size));) {
> >>>>>>>>>>>>>> +    IsAuthDataAssigned = FALSE;
> >>>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
> OffSet);
> >>>>>>>>>>>>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE
> >>>>>>>>>> (WinCertificate-
> >>>>>>>>>>>>> dwLength);
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> I disagree with this patch.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> The primary reason for my disagreement is that the bug report
> >>>>>>>>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is
> >>>>>>>>>>>>> inexact, and so this patch tries to fix the wrong thing.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> With edk2 master at commit 65904cdbb33c, it is *not* possible
> to
> >>>>>>>>>>>>> overflow the OffSet variable to zero with "WinCertificate-
> >>>>> dwLength"
> >>>>>>>>>>>>> *purely*, and cause an endless loop. Note that we have (at
> >> commit
> >>>>>>>>>>>>> 65904cdbb33c):
> >>>>>>>>>>>>>
> >>>>>>>>>>>>>   for (OffSet = SecDataDir->VirtualAddress;
> >>>>>>>>>>>>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>>>>>>>>>>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >>>>>>>>>>>>> (WinCertificate-
> >>>>>>>>>>>>> dwLength))) {
> >>>>>>>>>>>>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
> OffSet);
> >>>>>>>>>>>>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
> >>>>>>>>>>>>> <= sizeof
> >>>>>>>>>>>> (WIN_CERTIFICATE) ||
> >>>>>>>>>>>>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
> >>>>>>>>>> WinCertificate-
> >>>>>>>>>>>>> dwLength) {
> >>>>>>>>>>>>>       break;
> >>>>>>>>>>>>>     }
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> The last sub-condition checks whether the Security Data
> Directory
> >>>>>>>>>>>>> has enough room left for "WinCertificate->dwLength". If not,
> then
> >>>>>>>>>>>>> we break out of the loop.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> If we *do* have enough room, that is:
> >>>>>>>>>>>>>
> >>>>>>>>>>>>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >=
> >>>>>>>>>> WinCertificate-
> >>>>>>>>>>>>> dwLength
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> then we have (by adding OffSet to both sides):
> >>>>>>>>>>>>>
> >>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet +
> >>>>>>>>>>>>> WinCertificate- dwLength
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> The left hand side is a known-good UINT32, and so
> incrementing
> >>>>>>>>>>>>> OffSet (a
> >>>>>>>>>>>>> UINT32) *solely* by "WinCertificate->dwLength" (also a
> UINT32)
> >>>>>>>>>>>>> does not cause an overflow.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> Instead, the problem is with the alignment. The "if" statement
> >>>>>>>>>>>>> checks whether we have enough room for "dwLength", but
> then
> >>>>>>>>>>>>> "OffSet" is advanced by "dwLength" *aligned up* to the next
> >>>>>>>>>>>>> multiple of 8. And that may indeed cause various overflows.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> Now, the main problem with the present patch is that it does
> not
> >>>>>>>>>>>>> fix one of those overflows. Namely, consider that "dwLength" is
> >>>>>>>>>>>>> very close to
> >>>>>>>>>>>>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then
> >> aligning
> >>>>>>>>>>>>> it up to the next multiple of 8 will yield 0. In other words,
> >>>>>> "AlignedLength"
> >>>>>>>>>>>>> will be zero.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> And when that happens, there's going to be an infinite loop just
> >>>>>>>>>>>>> the
> >>>>>>>>>>>>> same: "OffSet" will not be zero, but it will be *stuck*. The
> >>>>>>>>>>>>> SafeUint32Add() call at the bottom will succeed, but it will not
> >>>>>>>>>>>>> change the value of "OffSet".
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> More at the bottom.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>>
> >>>>>>>>>>>>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
> >>>>>>>>>>>>>> <= sizeof
> >>>>>>>>>>>> (WIN_CERTIFICATE) ||
> >>>>>>>>>>>>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
> >>>>>>>>>>>>>> <
> >>>>>>>>>>>> WinCertificate->dwLength) {
> >>>>>>>>>>>>>>        break;
> >>>>>>>>>>>>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>>>        }
> >>>>>>>>>>>>>>        AuthData   = PkcsCertData->CertData;
> >>>>>>>>>>>>>>        AuthDataSize = PkcsCertData->Hdr.dwLength -
> >>>>>>>>>>>>>> sizeof(PkcsCertData-
> >>>>>>>>>>> Hdr);
> >>>>>>>>>>>>>> +      IsAuthDataAssigned = TRUE;
> >>>>>>>>>>>>>> +      HashStatus = HashPeImageByType (AuthData,
> AuthDataSize);
> >>>>>>>>>>>>>>      } else if (WinCertificate->wCertificateType ==
> >>>>>>>>>> WIN_CERT_TYPE_EFI_GUID)
> >>>>>>>>>>>> {
> >>>>>>>>>>>>>>        //
> >>>>>>>>>>>>>>        // The certificate is formatted as
> >>>>>>>>>>>>>> WIN_CERTIFICATE_UEFI_GUID which
> >>>>>>>>>> is
> >>>>>>>>>>>> described in UEFI Spec.
> >>>>>>>>>>>>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>>>        if (WinCertUefiGuid->Hdr.dwLength <=
> >>>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
> >>>>>>>>>>>>>>          break;
> >>>>>>>>>>>>>>        }
> >>>>>>>>>>>>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType,
> >>>>>> &gEfiCertPkcs7Guid))
> >>>>>>>>>> {
> >>>>>>>>>>>>>> -        continue;
> >>>>>>>>>>>>>> +      if (CompareGuid (&WinCertUefiGuid->CertType,
> >>>>>>>>>>>>>> + &gEfiCertPkcs7Guid))
> >>>>>>>>>> {
> >>>>>>>>>>>>>> +        AuthData = WinCertUefiGuid->CertData;
> >>>>>>>>>>>>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
> >>>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
> >>>>>>>>>>>>>> +        IsAuthDataAssigned = TRUE;
> >>>>>>>>>>>>>> +        HashStatus = HashPeImageByType (AuthData,
> >> AuthDataSize);
> >>>>>>>>>>>>>>        }
> >>>>>>>>>>>>>> -      AuthData = WinCertUefiGuid->CertData;
> >>>>>>>>>>>>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
> >>>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
> >>>>>>>>>>>>>>      } else {
> >>>>>>>>>>>>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE))
> {
> >>>>>>>>>>>>>>          break;
> >>>>>>>>>>>>>>        }
> >>>>>>>>>>>>>> -      continue;
> >>>>>>>>>>>>>>      }
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> -    HashStatus = HashPeImageByType (AuthData,
> AuthDataSize);
> >>>>>>>>>>>>>> -    if (EFI_ERROR (HashStatus)) {
> >>>>>>>>>>>>>> -      continue;
> >>>>>>>>>>>>>> -    }
> >>>>>>>>>>>>>> -
> >>>>>>>>>>>>>> -    //
> >>>>>>>>>>>>>> -    // Check the digital signature against the revoked
> certificate
> >> in
> >>>>>>>>>> forbidden
> >>>>>>>>>>>> database (dbx).
> >>>>>>>>>>>>>> -    //
> >>>>>>>>>>>>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> >>>>>>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> >>>>>>>>>>>>>> -      IsVerified = FALSE;
> >>>>>>>>>>>>>> -      break;
> >>>>>>>>>>>>>> -    }
> >>>>>>>>>>>>>> -
> >>>>>>>>>>>>>> -    //
> >>>>>>>>>>>>>> -    // Check the digital signature against the valid certificate in
> >>>>>> allowed
> >>>>>>>>>>>> database (db).
> >>>>>>>>>>>>>> -    //
> >>>>>>>>>>>>>> -    if (!IsVerified) {
> >>>>>>>>>>>>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
> >>>>>>>>>>>>>> -        IsVerified = TRUE;
> >>>>>>>>>>>>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
> >>>>>>>>>>>>>> +      //
> >>>>>>>>>>>>>> +      // Check the digital signature against the revoked
> >>>>>>>>>>>>>> + certificate in
> >>>>>>>>>> forbidden
> >>>>>>>>>>>> database (dbx).
> >>>>>>>>>>>>>> +      //
> >>>>>>>>>>>>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
> >>>>>>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
> >>>>>>>>>>>>>> +        IsVerified = FALSE;
> >>>>>>>>>>>>>> +        break;
> >>>>>>>>>>>>>>        }
> >>>>>>>>>>>>>> -    }
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> -    //
> >>>>>>>>>>>>>> -    // Check the image's hash value.
> >>>>>>>>>>>>>> -    //
> >>>>>>>>>>>>>> -    DbStatus = IsSignatureFoundInDatabase (
> >>>>>>>>>>>>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
> >>>>>>>>>>>>>> -                 mImageDigest,
> >>>>>>>>>>>>>> -                 &mCertType,
> >>>>>>>>>>>>>> -                 mImageDigestSize,
> >>>>>>>>>>>>>> -                 &IsFound
> >>>>>>>>>>>>>> -                 );
> >>>>>>>>>>>>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
> >>>>>>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> >>>>>>>>>>>>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image
> is
> >>>>>> signed
> >>>>>>>>>> but %s
> >>>>>>>>>>>> hash of image is found in DBX.\n", mHashTypeStr));
> >>>>>>>>>>>>>> -      IsVerified = FALSE;
> >>>>>>>>>>>>>> -      break;
> >>>>>>>>>>>>>> -    }
> >>>>>>>>>>>>>> +      //
> >>>>>>>>>>>>>> +      // Check the digital signature against the valid
> >>>>>>>>>>>>>> + certificate in allowed
> >>>>>>>>>>>> database (db).
> >>>>>>>>>>>>>> +      //
> >>>>>>>>>>>>>> +      if (!IsVerified) {
> >>>>>>>>>>>>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
> >>>>>>>>>>>>>> +          IsVerified = TRUE;
> >>>>>>>>>>>>>> +        }
> >>>>>>>>>>>>>> +      }
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> -    if (!IsVerified) {
> >>>>>>>>>>>>>> +      //
> >>>>>>>>>>>>>> +      // Check the image's hash value.
> >>>>>>>>>>>>>> +      //
> >>>>>>>>>>>>>>        DbStatus = IsSignatureFoundInDatabase (
> >>>>>>>>>>>>>> -                   EFI_IMAGE_SECURITY_DATABASE,
> >>>>>>>>>>>>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
> >>>>>>>>>>>>>>                     mImageDigest,
> >>>>>>>>>>>>>>                     &mCertType,
> >>>>>>>>>>>>>>                     mImageDigestSize,
> >>>>>>>>>>>>>>                     &IsFound
> >>>>>>>>>>>>>>                     );
> >>>>>>>>>>>>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
> >>>>>>>>>>>>>> -        IsVerified = TRUE;
> >>>>>>>>>>>>>> -      } else {
> >>>>>>>>>>>>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image
> is
> >>>>>> signed
> >>>>>>>>>> but
> >>>>>>>>>>>> signature is not allowed by DB and %s hash of image is not found
> in
> >>>>>>>>>> DB/DBX.\n",
> >>>>>>>>>>>> mHashTypeStr));
> >>>>>>>>>>>>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
> >>>>>>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
> >>>>>>>>>>>>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image
> is
> >>>>>>>>>>>>>> + signed
> >>>>>>>>>>>> but %s hash of image is found in DBX.\n", mHashTypeStr));
> >>>>>>>>>>>>>> +        IsVerified = FALSE;
> >>>>>>>>>>>>>> +        break;
> >>>>>>>>>>>>>>        }
> >>>>>>>>>>>>>> +
> >>>>>>>>>>>>>> +      if (!IsVerified) {
> >>>>>>>>>>>>>> +        DbStatus = IsSignatureFoundInDatabase (
> >>>>>>>>>>>>>> +                     EFI_IMAGE_SECURITY_DATABASE,
> >>>>>>>>>>>>>> +                     mImageDigest,
> >>>>>>>>>>>>>> +                     &mCertType,
> >>>>>>>>>>>>>> +                     mImageDigestSize,
> >>>>>>>>>>>>>> +                     &IsFound
> >>>>>>>>>>>>>> +                     );
> >>>>>>>>>>>>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
> >>>>>>>>>>>>>> +          IsVerified = TRUE;
> >>>>>>>>>>>>>> +        } else {
> >>>>>>>>>>>>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib:
> Image
> >> is
> >>>>>>>>>>>>>> + signed
> >>>>>>>>>> but
> >>>>>>>>>>>> signature is not allowed by DB and %s hash of image is not found
> in
> >>>>>>>>>> DB/DBX.\n",
> >>>>>>>>>>>> mHashTypeStr));
> >>>>>>>>>>>>>> +        }
> >>>>>>>>>>>>>> +      }
> >>>>>>>>>>>>>> +    }
> >>>>>>>>>>>>>> +
> >>>>>>>>>>>>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength,
> &Result);
> >>>>>>>>>>>>>> +    if (EFI_ERROR (AddStatus)) {
> >>>>>>>>>>>>>> +      break;
> >>>>>>>>>>>>>>      }
> >>>>>>>>>>>>>> +    OffSet = Result;
> >>>>>>>>>>>>>>    }
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
> >>>>>>>>>>>>>> {
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> There are other (smaller) reasons why I dislike this patch:
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> - The "IsAuthDataAssigned" variable is superfluous; we could
> use
> >>>>>>>>>>>>> the existent "AuthData" variable (with a NULL-check and a
> >>>>>>>>>>>>> NULL-assignment) similarly.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> - The patch complicates / reorganizes the control flow
> needlessly.
> >>>>>>>>>>>>> This complication originates from placing the checked "OffSet"
> >>>>>>>>>>>>> increment at the bottom of the loop, which then requires the
> >>>>>>>>>>>>> removal of all the "continue" statements. But we don't need to
> >>>>>>>>>>>>> check-and-increment at the bottom. We can keep the
> increment
> >>>>>>>>>>>>> inside the "for" statement, only extend the *existent* room
> check
> >>>>>>>>>>>>> (which I've quoted) to take the alignment into account as well.
> If
> >>>>>>>>>>>>> there is enough room for the alignment in the security data
> >>>>>>>>>>>>> directory, then that guarantees there won't be a UINT32
> overflow
> >>>>>> either.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> All in all, I'm proposing the following three patches instead. The
> >>>>>>>>>>>>> first two patches are preparation, the last patch is the fix.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> Patch#1:
> >>>>>>>>>>>>>
> >>>>>>>>>>>>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon
> Sep
> >> 17
> >>>>>>>>>> 00:00:00
> >>>>>>>>>>>> 2001
> >>>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
> >>>>>>>>>>>>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib:
> >> extract
> >>>>>>>>>>>>>> SecDataDirEnd, SecDataDirLeft
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> The following two quantities:
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
> >>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> are used multiple times in DxeImageVerificationHandler().
> >>>>>>>>>>>>>> Introduce helper variables for them: "SecDataDirEnd" and
> >>>>>> "SecDataDirLeft", respectively.
> >>>>>>>>>>>>>> This saves us multiple calculations and significantly simplifies
> the
> >>>> code.
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> Note that all three summands above have type UINT32,
> >> therefore
> >>>>>>>>>>>>>> the new variables are also of type UINT32.
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> This patch does not change behavior.
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> (Note that the code already handles the case when the
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> UINT32 addition overflows -- namely, in that case, the
> >>>>>>>>>>>>>> certificate loop is never entered, and the corruption check
> right
> >>>>>>>>>>>>>> after the loop fires.)
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>>>>>> ---
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>>
> >>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>>>> ib.c |
> >>>>>>>>>> 12
> >>>>>>>>>>>> ++++++++----
> >>>>>>>>>>>>>>  1 file changed, 8 insertions(+), 4 deletions(-)
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> diff --git
> >>>>>>>>>>>>
> >>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.c
> >>>>>>>>>>>>
> >>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.c
> >>>>>>>>>>>>>> index 36b87e16d53d..8761980c88aa 100644
> >>>>>>>>>>>>>> ---
> >>>>>>>>>>
> >>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
> >>>>>>>>>> .c
> >>>>>>>>>>>>>> +++
> >>>>>>>>>>>>
> >>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.c
> >>>>>>>>>>>>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>>>    UINT8                                *AuthData;
> >>>>>>>>>>>>>>    UINTN                                AuthDataSize;
> >>>>>>>>>>>>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
> >>>>>>>>>>>>>> +  UINT32                               SecDataDirEnd;
> >>>>>>>>>>>>>> +  UINT32                               SecDataDirLeft;
> >>>>>>>>>>>>>>    UINT32                               OffSet;
> >>>>>>>>>>>>>>    CHAR16                               *NameStr;
> >>>>>>>>>>>>>>    RETURN_STATUS                        PeCoffStatus;
> >>>>>>>>>>>>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>>>    // "Attribute Certificate Table".
> >>>>>>>>>>>>>>    // The first certificate starts at offset
> >>>>>>>>>>>>>> (SecDataDir->VirtualAddress) from
> >>>>>>>>>> the
> >>>>>>>>>>>> start of the file.
> >>>>>>>>>>>>>>    //
> >>>>>>>>>>>>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir-
> >>> Size;
> >>>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>>>>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
> >>>>>>>>>>>>>> +       OffSet < SecDataDirEnd;
> >>>>>>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >>>>>>>>>>>>>> (WinCertificate-
> >>>>>>>>>>>>> dwLength))) {
> >>>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
> OffSet);
> >>>>>>>>>>>>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
> <=
> >>>>>> sizeof
> >>>>>>>>>>>> (WIN_CERTIFICATE) ||
> >>>>>>>>>>>>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
> <
> >>>>>>>>>>>> WinCertificate->dwLength) {
> >>>>>>>>>>>>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
> >>>>>>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
> >>>>>>>>>>>>>> +        SecDataDirLeft < WinCertificate->dwLength) {
> >>>>>>>>>>>>>>        break;
> >>>>>>>>>>>>>>      }
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>>>      }
> >>>>>>>>>>>>>>    }
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
> >>>>>>>>>>>>>> {
> >>>>>>>>>>>>>> +  if (OffSet != SecDataDirEnd) {
> >>>>>>>>>>>>>>      //
> >>>>>>>>>>>>>>      // The Size in Certificate Table or the attribute
> >>>>>>>>>>>>>> certificate table is
> >>>>>>>>>> corrupted.
> >>>>>>>>>>>>>>      //
> >>>>>>>>>>>>>> --
> >>>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> Patch#2:
> >>>>>>>>>>>>>
> >>>>>>>>>>>>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep
> >> 17
> >>>>>> 00:00:00
> >>>>>>>>>>>> 2001
> >>>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
> >>>>>>>>>>>>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib:
> >> assign
> >>>>>>>>>>>>>> WinCertificate after size check
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE))
> >> check
> >>>>>>>>>>>>>> only guards the de-referencing of the "WinCertificate" pointer.
> >>>>>>>>>>>>>> It does not guard the calculation of hte pointer itself:
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
> OffSet);
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> This is wrong; if we don't know for sure that we have enough
> >> room
> >>>>>>>>>>>>>> for a WIN_CERTIFICATE, then even creating such a pointer,
> not
> >>>>>>>>>>>>>> just de-referencing it, may invoke undefined behavior.
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> Move the pointer calculation after the size check.
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>>>>>> ---
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>>
> >>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>>>> ib.c |
> >>>>>>>>>> 8
> >>>>>>>>>>>> +++++---
> >>>>>>>>>>>>>>  1 file changed, 5 insertions(+), 3 deletions(-)
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> diff --git
> >>>>>>>>>>>>
> >>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.c
> >>>>>>>>>>>>
> >>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.c
> >>>>>>>>>>>>>> index 8761980c88aa..461ed7cfb5ac 100644
> >>>>>>>>>>>>>> ---
> >>>>>>>>>>
> >>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
> >>>>>>>>>> .c
> >>>>>>>>>>>>>> +++
> >>>>>>>>>>>>
> >>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.c
> >>>>>>>>>>>>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
> >>>>>>>>>>>>>>         OffSet < SecDataDirEnd;
> >>>>>>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
> >>>>>>>>>>>>>> (WinCertificate-
> >>>>>>>>>>>>> dwLength))) {
> >>>>>>>>>>>>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
> >> OffSet);
> >>>>>>>>>>>>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
> >>>>>>>>>>>>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
> >>>>>>>>>>>>>> -        SecDataDirLeft < WinCertificate->dwLength) {
> >>>>>>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
> >>>>>>>>>>>>>> +      break;
> >>>>>>>>>>>>>> +    }
> >>>>>>>>>>>>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
> >> OffSet);
> >>>>>>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
> >>>>>>>>>>>>>>        break;
> >>>>>>>>>>>>>>      }
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> --
> >>>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> Patch#3:
> >>>>>>>>>>>>>
> >>>>>>>>>>>>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep
> 17
> >>>>>> 00:00:00
> >>>>>>>>>>>> 2001
> >>>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
> >>>>>>>>>>>>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib:
> catch
> >>>>>>>>>> alignment
> >>>>>>>>>>>>>>  overflow (CVE-2019-14562)
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> The DxeImageVerificationHandler() function currently checks
> >>>>>>>>>>>>>> whether "SecDataDir" has enough room for
> >>>>>>>>>>>>>> "WinCertificate->dwLength". However,
> >>>>>>>>>>>> for
> >>>>>>>>>>>>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to
> the
> >>>>>>>>>>>>>> next multiple of 8. If "WinCertificate->dwLength" is large
> >>>>>>>>>>>>>> enough, the alignment will return 0, and "OffSet" will be stuck
> at
> >>>> the
> >>>>>> same value.
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> Check whether "SecDataDir" has room left for both
> >>>>>>>>>>>>>> "WinCertificate->dwLength" and the alignment.
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> >>>>>>>>>>>>>> ---
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>>
> >>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>>>> ib.c |
> >>>>>>>>>> 4
> >>>>>>>>>>>> +++-
> >>>>>>>>>>>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> diff --git
> >>>>>>>>>>>>
> >>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.c
> >>>>>>>>>>>>
> >>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.c
> >>>>>>>>>>>>>> index 461ed7cfb5ac..e38eb981b7a0 100644
> >>>>>>>>>>>>>> ---
> >>>>>>>>>>
> >>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
> >>>>>>>>>> .c
> >>>>>>>>>>>>>> +++
> >>>>>>>>>>>>
> >>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
> >>>>>>>>>>>> ib.c
> >>>>>>>>>>>>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
> >>>>>>>>>>>>>>        break;
> >>>>>>>>>>>>>>      }
> >>>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
> OffSet);
> >>>>>>>>>>>>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
> >>>>>>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
> >>>>>>>>>>>>>> +        (SecDataDirLeft - WinCertificate->dwLength <
> >>>>>>>>>>>>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
> >>>>>>>>>>>>>>        break;
> >>>>>>>>>>>>>>      }
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> --
> >>>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> If Wenyi and the reviewers are OK with these patches, I can
> >> submit
> >>>>>>>>>>>>> them as a standalone patch series.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> Note that I do not have any reproducer for the issue; the best
> >>>>>>>>>>>>> testing that I could offer would be some light-weight Secure
> Boot
> >>>>>>>>>>>>> regression tests.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> Thanks
> >>>>>>>>>>>>> Laszlo
> >>>>>>>>>>>>>
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> .
> >>>>>>>>>>>>>
> >>>>>>>>>>>>
> >>>>>>>>>>>>
> >>>>>>>>>>>>
> >>>>>>>>>>>
> >>>>>>>>>>
> >>>>>>>>>>
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>
> >>>>>>>
> >>>>>>>
> >>>>>>> .
> >>>>>>>
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>>
> >>>>
> >>>>
> >>>>
> >>>
> >>
> >>
> >>
> >
> 
> 
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-09-01  7:31                                 ` Yao, Jiewen
@ 2020-09-01  7:43                                   ` wenyi,xie
  2020-09-01  7:56                                     ` Laszlo Ersek
  0 siblings, 1 reply; 32+ messages in thread
From: wenyi,xie @ 2020-09-01  7:43 UTC (permalink / raw)
  To: Yao, Jiewen, devel@edk2.groups.io, Laszlo Ersek, Wang, Jian J
  Cc: songdongkuang@huawei.com, Mathews, John

To Jiewen,

Sorry to make you lost,I mean the patches Laszlo proposed in email,
https://edk2.groups.io/g/devel/message/64243
http://mid.mail-archive.com/eb0c6bcb-77fb-2fb9-783e-aa5025953a80@redhat.com

To Laszlo,

Sure, I will test your patches against my reproducer and add "tested-by:" tag to the patches after you post them.

Regards
Wenyi

On 2020/9/1 15:31, Yao, Jiewen wrote:
> I am sorry, that I am a little lost here.
> 
> We have discussed different patches. I am not 100% sure which one is "Laszlo's patches".
> 
> To make thing easy and record all actions, would you please reply the patch(es) you have verified, with your "tested-by:" tag?
> 
> Thank you
> Yao Jiewen
> 
>> -----Original Message-----
>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of wenyi,xie
>> via groups.io
>> Sent: Tuesday, September 1, 2020 3:11 PM
>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Laszlo Ersek
>> <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>
>> Cc: songdongkuang@huawei.com; Mathews, John <john.mathews@intel.com>
>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>
>> I think Laszlo's patches is OK, I have applied and tested it using my case. It can
>> catch the issue.
>> DEBUG code and log below,
>>
>>   SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
>>   DEBUG((DEBUG_INFO, "SecDataDirEnd=0x%x.\n", SecDataDirEnd));
>>   for (OffSet = SecDataDir->VirtualAddress;
>>        OffSet < SecDataDirEnd;
>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>> dwLength))) {
>>     SecDataDirLeft = SecDataDirEnd - OffSet;
>>     DEBUG((DEBUG_INFO, "OffSet=0x%x, SecDataDirLeft=0x%x.\n", OffSet,
>> SecDataDirLeft));
>>     if (SecDataDirLeft <= sizeof(WIN_CERTIFICATE)) {
>>       break;
>>     }
>>     WinCertificate = (WIN_CERTIFICATE *)(mImageBase + OffSet);
>>     DEBUG((DEBUG_INFO, "WinCertificate->dwLength=0x%x, ALIGN_SIZE
>> (WinCertificate->dwLength)=0x%x.\n", WinCertificate->dwLength,
>> ALIGN_SIZE(WinCertificate->dwLength)));
>>     if (SecDataDirLeft < WinCertificate->dwLength ||
>> 	(SecDataDirLeft - WinCertificate->dwLength <
>> ALIGN_SIZE(WinCertificate->dwLength))) {
>>       DEBUG((DEBUG_INFO, "Parameter is invalid and break.\n"));
>>       break;
>>     }
>>
>> SecDataDirEnd=0xFFFFFFFC.
>> OffSet=0xE000, SecDataDirLeft=0xFFFF1FFC.
>> WinCertificate->dwLength=0xFFFF1FFB, ALIGN_SIZE (WinCertificate-
>>> dwLength)=0x5.
>> Parameter is invalid and break.
>> The image doesn't pass verification: VenHw(5CF32E0B-8EDF-2E44-9CDA-
>> 93205E99EC1C,00000000)/VenHw(964E5B22-6459-11D2-8E39-
>> 00A0C969723B,00000000)/\signed_1234_4G.efi
>>
>> Regards
>> Wenyi
>>
>> On 2020/9/1 0:06, Yao, Jiewen wrote:
>>> Sounds great. Appreciate your hard work on that.
>>>
>>> Will you post a patch to fix the issue again?
>>>
>>> Thank you
>>> Yao Jiewen
>>>
>>>> -----Original Message-----
>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>> wenyi,xie
>>>> via groups.io
>>>> Sent: Monday, August 31, 2020 7:24 PM
>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Laszlo
>> Ersek
>>>> <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>
>>>> Cc: songdongkuang@huawei.com; Mathews, John
>> <john.mathews@intel.com>
>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>
>>>> Hi,Jiewen,
>>>>
>>>> I modify the PE file again, this time it can pass the check in PeCoffLib and
>> cause
>>>> offset overflow.
>>>>
>>>> First, create a PE file and sign it(only one signature), then using binary edit
>> tool
>>>> to modify content of PE file like below,
>>>>  1.check the value of SecDataDir->VirtualAddress, in my PE file, it's 0xE000
>>>>  2.changing SecDataDir->Size from 0x5F8 to 0xFFFF1FFC
>>>>  3.changing WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFB
>>>>  4.padding PE file with 0 until the size of the file is 0xFFFFFFFC(it will make the
>> PE
>>>> file so large)
>>>> OffSet + WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength)
>> is
>>>> 0xE000 + 0xFFFF1FFB + 0x5 = 0x100000000
>>>>
>>>> Below is the DEBUG code and log, in second loop the offset overflow and
>>>> become 0
>>>>
>>>> for (OffSet = SecDataDir->VirtualAddress;
>>>>      OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>      OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>>>> dwLength))) {
>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>   DEBUG((DEBUG_INFO, "OffSet=0x%x.\n", OffSet));
>>>>   if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>>>> (WIN_CERTIFICATE) ||
>>>>       (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
>>>>> dwLength) {
>>>>     break;
>>>>   }
>>>>   DEBUG((DEBUG_INFO, "WinCertificate->dwLength=0x%x, ALIGN_SIZE
>>>> (WinCertificate->dwLength)=0x%x.\n", WinCertificate->dwLength,
>>>> ALIGN_SIZE(WinCertificate->dwLength)));
>>>>
>>>>
>>>> SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFC.
>>>> OffSet=0xE000.
>>>> WinCertificate->dwLength=0xFFFF1FFB, ALIGN_SIZE (WinCertificate-
>>>>> dwLength)=0x5.
>>>> DxeImageVerificationLib: Image is signed but signature is not allowed by DB
>> and
>>>> SHA256 hash of image is notOffSet=0x0.
>>>> WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate-
>>>>> dwLength)=0x3.
>>>> OffSet=0x5A50.
>>>> WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate-
>>>>> dwLength)=0x4.
>>>> OffSet=0xEA78.
>>>> WinCertificate->dwLength=0x0, ALIGN_SIZE (WinCertificate->dwLength)=0x0.
>>>> The image doesn't pass verification: VenHw(5CF32E0B-8EDF-2E44-9CDA-
>>>> 93205E99EC1C,00000000)/VenHw(964E5B22-6459-11D2-8E39-
>>>> 00A0C969723B,00000000)/\signed_1234_4G.efi
>>>>
>>>>
>>>> Regards
>>>> Wenyi
>>>>
>>>>
>>>> On 2020/8/28 14:43, Yao, Jiewen wrote:
>>>>> Apology that I did not say clearly.
>>>>> I mean you should not modify any code to perform an attack.
>>>>>
>>>>> I am not asking you to exploit the system. Attack here just means: to cause
>>>> system hang or buffer overflow. That is enough.
>>>>> But you cannot modify code to remove any existing checker.
>>>>>
>>>>> Thank you
>>>>> Yao Jiewen
>>>>>
>>>>>> -----Original Message-----
>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>>>> wenyi,xie
>>>>>> via groups.io
>>>>>> Sent: Friday, August 28, 2020 2:18 PM
>>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Laszlo
>>>> Ersek
>>>>>> <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>
>>>>>> Cc: songdongkuang@huawei.com; Mathews, John
>>>> <john.mathews@intel.com>
>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>
>>>>>> Hi,Jiewen,
>>>>>>
>>>>>> I don't really get the meaning "create a case that bypass all checks in
>>>> PeCoffLib",
>>>>>> do you mean I need to create a PE file that can bypass all check in
>> PeCoffLib
>>>>>> without modify any
>>>>>> code and then cause the problem in DxeImageVerificationLib, or just
>> modify
>>>>>> some code in PeCoffLib to bypass check instead of removing the calling of
>>>>>> PeCoffLoaderGetImageInfo. Would
>>>>>> you mind explaining a little more specifically? As far as I tried, it's really
>> hard
>>>> to
>>>>>> reproduce the issue without touching any code.
>>>>>>
>>>>>> Thanks
>>>>>> Wenyi
>>>>>>
>>>>>> On 2020/8/28 11:50, Yao, Jiewen wrote:
>>>>>>> HI Wenyi
>>>>>>> Thank you very much to take time to reproduce.
>>>>>>>
>>>>>>> I am particular interested in below:
>>>>>>> 	"As PE file is modified, function PeCoffLoaderGetImageInfo will return
>>>>>> error, so I have to remove it so that for loop can be tested in
>>>>>> DxeImageVerificationHandler."
>>>>>>>
>>>>>>> By design, the PeCoffLib should catch illegal PE/COFF image and return
>> error.
>>>>>> (even it cannot catch all, it should catch most ones).
>>>>>>> Other PE/COFF parser may rely on the checker in PeCoffLib and *no
>> need*
>>>> to
>>>>>> duplicate all checkers.
>>>>>>> As such, DxeImageVerificationLib (and other PeCoff consumer) just need
>>>>>> checks what has passed the check in PeCoffLib.
>>>>>>>
>>>>>>> I don’t think you should remove the checker. If people can remove the
>>>> checker,
>>>>>> I am sure the rest code will be vulnerable, according to the current design.
>>>>>>> Could you please to create a case that bypass all checks in PeCoffLib,
>> then
>>>> run
>>>>>> into DxeImageVerificationLib and cause the problem? That would be more
>>>>>> valuable for us.
>>>>>>>
>>>>>>> Thank you
>>>>>>> Yao Jiewen
>>>>>>>
>>>>>>>> -----Original Message-----
>>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>>>>>> wenyi,xie
>>>>>>>> via groups.io
>>>>>>>> Sent: Friday, August 28, 2020 11:18 AM
>>>>>>>> To: Laszlo Ersek <lersek@redhat.com>; Wang, Jian J
>>>>>> <jian.j.wang@intel.com>;
>>>>>>>> devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>
>>>>>>>> Cc: songdongkuang@huawei.com; Mathews, John
>>>>>> <john.mathews@intel.com>
>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>>>
>>>>>>>> Hi,Laszlo and everyone,
>>>>>>>>
>>>>>>>> These days I tried to reproduce the issue,and made some progress. I
>>>> think
>>>>>>>> there are two way to cause overflow from a mathematical point of view,
>>>>>>>> 1.As Laszlo analysed before, if WinCertificate->dwLength is large
>> enough,
>>>>>> close
>>>>>>>> to MAX_UINT32, then (WinCertificate->dwLength + ALIGN_SIZE
>>>>>> (WinCertificate-
>>>>>>>>> dwLength)) will cause overflow.
>>>>>>>> 2.(WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))
>> is
>>>>>> good,
>>>>>>>> OffSet is good, but OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>> (WinCertificate->dwLength)) cause overflow.
>>>>>>>>
>>>>>>>> Here I choose the second way to reproduce the issue, I choose a PE file
>>>> and
>>>>>> sign
>>>>>>>> it with my own db certificate. Then I use binary edit tool to modify the
>> PE
>>>> file
>>>>>> like
>>>>>>>> below,
>>>>>>>>
>>>>>>>> 1.change SecDataDir->Size from 0x5F8 to 0xFFFF1FFF
>>>>>>>> 2.change WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFE
>>>>>>>> SecDataDir->VirtualAddress in my PE is 0xe000 and no need to change.
>>>>>>>>
>>>>>>>> As PE file is modified, function PeCoffLoaderGetImageInfo will return
>> error,
>>>>>> so I
>>>>>>>> have to remove it so that for loop can be tested in
>>>>>> DxeImageVerificationHandler.
>>>>>>>> The log is as below,
>>>>>>>>
>>>>>>>> SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFF.
>>>>>>>> (First Loop)
>>>>>>>> OffSet=0xE000.
>>>>>>>> WinCertificate->dwLength=0xFFFF1FFE, ALIGN_SIZE (WinCertificate-
>>>>>>>>> dwLength)=0x2.
>>>>>>>> (Second Loop)
>>>>>>>> OffSet=0x0.
>>>>>>>> WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate-
>>>>>>>>> dwLength)=0x3.
>>>>>>>> (Third Loop)
>>>>>>>> OffSet=0x5A50.
>>>>>>>> WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate-
>>>>>>>>> dwLength)=0x4.
>>>>>>>> (Forth Loop)
>>>>>>>> OffSet=0xEA78.
>>>>>>>> WinCertificate->dwLength=0xAFAFAFAF, ALIGN_SIZE (WinCertificate-
>>>>>>>>> dwLength)=0x1.
>>>>>>>> (Fifth Loop)
>>>>>>>> OffSet=0xAFB09A28.
>>>>>>>>
>>>>>>>> As I modify SecDataDir->Size and WinCertificate->dwLength, so in first
>>>> loop
>>>>>> the
>>>>>>>> condition check whether the Security Data Directory has enough room
>> left
>>>>>> for
>>>>>>>> "WinCertificate->dwLength" is ok.
>>>>>>>>
>>>>>>>> if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>     (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>> WinCertificate-
>>>>>>>>> dwLength) {
>>>>>>>>   break;
>>>>>>>> }
>>>>>>>>
>>>>>>>> In the beginning of second loop, WinCertificate->dwLength +
>> ALIGN_SIZE
>>>>>>>> (WinCertificate->dwLength) is 0xFFFF2000, offset is 0xE000
>>>>>>>>
>>>>>>>> OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>>>>>> dwLength))
>>>>>>>>
>>>>>>>> Offset now is 0 and overflow happens. So even if my PE only have one
>>>>>> signature,
>>>>>>>> the for loop is still going untill exception happens.
>>>>>>>>
>>>>>>>>
>>>>>>>> I can't reproduce the issue using the first way, because if WinCertificate-
>>>>>>>>> dwLength is close to MAX_UINT32, it means SecDataDir->Size should
>> also
>>>>>> close
>>>>>>>> to MAX_UINT32, or the condition check
>>>>>>>> "(SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>> WinCertificate-
>>>>>>>>> dwLength" will break. But if SecDataDir->Size is very large, SecDataDir-
>>>>>>>>> VirtualAddress have to be smaller than 8 bytes,
>>>>>>>> because SecDataDir->VirtualAddress + SecDataDir->Size < MAX_UINT32.
>>>>>>>> SecDataDir->VirtualAddress is the beginning address of the signature,
>> and
>>>>>> before
>>>>>>>> SecDataDir->VirtualAddress is the content of origin PE file, I think it's
>>>>>> impossible
>>>>>>>> that the size of PE file is only 8 bytes.
>>>>>>>>
>>>>>>>> As I changed the one line code in DxeImageVerificationHandler to
>>>> reproduce
>>>>>> the
>>>>>>>> issue, I'm not sure whether it's ok.
>>>>>>>>
>>>>>>>> Thanks
>>>>>>>> Wenyi
>>>>>>>>
>>>>>>>> On 2020/8/19 17:26, Laszlo Ersek wrote:
>>>>>>>>> On 08/18/20 17:18, Mathews, John wrote:
>>>>>>>>>> I dug up the original report details.  This was noted as a concern
>> during a
>>>>>>>> source code inspection.  There was no demonstration of how it might be
>>>>>>>> triggered.
>>>>>>>>>>
>>>>>>>>>> " There is an integer overflow vulnerability in the
>>>>>>>> DxeImageVerificationHandler function when
>>>>>>>>>> parsing the PE files attribute certificate table. In cases where
>>>>>> WinCertificate-
>>>>>>>>> dwLength is
>>>>>>>>>> sufficiently large, it's possible to overflow Offset back to 0 causing an
>>>>>> endless
>>>>>>>> loop."
>>>>>>>>>>
>>>>>>>>>> The recommendation was to add stricter checking of "Offset" and the
>>>>>>>> embedded length fields of certificate data
>>>>>>>>>> before using them.
>>>>>>>>>
>>>>>>>>> Thanks for checking!
>>>>>>>>>
>>>>>>>>> Laszlo
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> -----Original Message-----
>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>> Sent: Tuesday, August 18, 2020 1:59 AM
>>>>>>>>>> To: Wang, Jian J <jian.j.wang@intel.com>; devel@edk2.groups.io;
>> Yao,
>>>>>>>> Jiewen <jiewen.yao@intel.com>; xiewenyi2@huawei.com
>>>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com;
>>>> Mathews,
>>>>>>>> John <john.mathews@intel.com>
>>>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>>>>>
>>>>>>>>>> On 08/18/20 04:10, Wang, Jian J wrote:
>>>>>>>>>>> Laszlo,
>>>>>>>>>>>
>>>>>>>>>>> My apologies for the slow response. I'm not the original reporter but
>>>>>>>>>>> just the BZ submitter. And I didn't do deep analysis to this issue.
>>>>>>>>>>> The issues was reported from one internal team. Add John in loop to
>>>> see
>>>>>> if
>>>>>>>> he knows more about it or not.
>>>>>>>>>>>
>>>>>>>>>>> My superficial understanding on such issue is that, if there's
>>>>>>>>>>> "potential" issue in theory and hard to reproduce, it's still worthy
>>>>>>>>>>> of using an alternative way to replace the original implementation
>>>>>>>>>>> with no "potential" issue at all. Maybe we don't have to prove old
>> way
>>>> is
>>>>>>>> something wrong but must prove that the new way is really safe.
>>>>>>>>>>
>>>>>>>>>> I agree, thanks.
>>>>>>>>>>
>>>>>>>>>> It would be nice to hear more from the internal team about the
>>>> originally
>>>>>>>> reported (even if hard-to-trigger) issue.
>>>>>>>>>>
>>>>>>>>>> Thanks!
>>>>>>>>>> Laszlo
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Regards,
>>>>>>>>>>> Jian
>>>>>>>>>>>
>>>>>>>>>>>> -----Original Message-----
>>>>>>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf
>> Of
>>>>>> Laszlo
>>>>>>>>>>>> Ersek
>>>>>>>>>>>> Sent: Tuesday, August 18, 2020 12:53 AM
>>>>>>>>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
>>>>>>>>>>>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
>>>>>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>>>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of
>> Offset
>>>>>>>>>>>>
>>>>>>>>>>>> Hi Jiewen,
>>>>>>>>>>>>
>>>>>>>>>>>> On 08/14/20 10:53, Yao, Jiewen wrote:
>>>>>>>>>>>>>> To Jiewen,
>>>>>>>>>>>>>> Sorry, I don't have environment to reproduce the issue.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Please help me understand, if you don’t have environment to
>>>>>>>>>>>>> reproduce the
>>>>>>>>>>>> issue, how do you guarantee that your patch does fix the problem
>> and
>>>>>>>>>>>> we don’t have any other vulnerabilities?
>>>>>>>>>>>>
>>>>>>>>>>>> The original bug report in
>>>>>>>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is
>>>> seriously
>>>>>>>>>>>> lacking. It does not go into detail about the alleged integer
>> overflow.
>>>>>>>>>>>> It does not quote the code, does not explain the control flow, does
>>>>>>>>>>>> not identify the exact edk2 commit at which the vulnerability exists.
>>>>>>>>>>>>
>>>>>>>>>>>> The bug report also does not offer a reproducer.
>>>>>>>>>>>>
>>>>>>>>>>>> Additionally, the exact statement that the bug report does make,
>>>>>>>>>>>> namely
>>>>>>>>>>>>
>>>>>>>>>>>>   it's possible to overflow Offset back to 0 causing an endless loop
>>>>>>>>>>>>
>>>>>>>>>>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can
>>>>>>>>>>>> be overflowed to zero, but the *addend* that is added to OffSet
>> can
>>>>>>>>>>>> be overflowed to zero. Therefore the infinite loop will arise
>> because
>>>>>>>>>>>> OffSet remains stuck at its present value, and not because OffSet
>>>>>>>>>>>> will be re-set to zero.
>>>>>>>>>>>>
>>>>>>>>>>>> For the reasons, we can only speculate as to what the actual
>> problem
>>>>>>>>>>>> is, unless Jian decides to join the discussion and clarifies what he
>>>>>>>>>>>> had in mind originally.
>>>>>>>>>>>>
>>>>>>>>>>>> My understanding (or even "reconstruction") of the vulnerability is
>>>>>>>>>>>> described above, and in the patches that I proposed.
>>>>>>>>>>>>
>>>>>>>>>>>> We can write a patch based on code analysis. It's possible to
>>>>>>>>>>>> identify integer overflows based on code analysis, and it's possible
>>>>>>>>>>>> to verify the correctness of fixes by code review. Obviously testing
>>>>>>>>>>>> is always good, but many times, constructing reproducers for such
>>>>>>>>>>>> issues that were found by code review, is difficult and time
>>>>>>>>>>>> consuming. We can say that we don't fix vulnerabilities without
>>>>>>>>>>>> reproducers, or we can say that we make an effort to fix them even
>> if
>>>>>>>>>>>> all we have is code analysis (and not a reproducer).
>>>>>>>>>>>>
>>>>>>>>>>>> So the above paragraph concerns "correctness". Regarding
>>>>>>>>>>>> "completeness", I guarantee you that this patch does not fix *all*
>>>>>>>>>>>> problems related to PE parsing. (See the other BZ tickets.) It does
>>>>>>>>>>>> fix *one* issue with PE parsing. We can say that we try to fix such
>>>>>>>>>>>> issues gradually (give different CVE numbers to different issues, and
>>>>>>>>>>>> address them one at a time), or we can say that we rewrite PE
>> parsing
>>>>>>>> from the ground up.
>>>>>>>>>>>> (BTW: I have seriously attempted that in the past, and I gave up,
>>>>>>>>>>>> because the PE format is FUBAR.)
>>>>>>>>>>>>
>>>>>>>>>>>> In summary:
>>>>>>>>>>>>
>>>>>>>>>>>> - the problem statement is unclear,
>>>>>>>>>>>>
>>>>>>>>>>>> - it seems like there is indeed an integer overflow problem in the
>>>>>>>>>>>> SecDataDir parsing loop, but it's uncertain whether the bug
>> reporter
>>>>>>>>>>>> had exactly that in mind
>>>>>>>>>>>>
>>>>>>>>>>>> - PE parsing is guaranteed to have other vulnerabilities elsewhere in
>>>>>>>>>>>> edk2, but I'm currently unaware of other such issues in
>>>>>>>>>>>> DxeImageVerificationLib specifically
>>>>>>>>>>>>
>>>>>>>>>>>> - even if there are other such problems (in DxeImageVerificationLib
>>>>>>>>>>>> or elswehere), fixing this bug that we know about is likely
>>>>>>>>>>>> worthwhile
>>>>>>>>>>>>
>>>>>>>>>>>> - for many such bugs, constructing a reproducer is difficult and time
>>>>>>>>>>>> consuming; code analysis, and *regression-testing* are frequently
>> the
>>>>>>>>>>>> only tools we have. That doesn't mean we should ignore this class
>> of
>>>>>> bugs.
>>>>>>>>>>>>
>>>>>>>>>>>> (Fixing integer overflows retro-actively is more difficult than
>>>>>>>>>>>> writing overflow-free code in the first place, but that ship has
>>>>>>>>>>>> sailed; so we can only fight these bugs incrementally now, unless
>> we
>>>>>>>>>>>> can rewrite PE parsing with a new data structure from the ground
>> up.
>>>>>>>>>>>> Again I tried that and gave up, because the spec is not public, and
>>>>>>>>>>>> what I did manage to learn about PE, showed that it was insanely
>>>>>>>>>>>> over-engineered. I'm not saying that other binary / executable
>>>>>>>>>>>> formats are better, of course.)
>>>>>>>>>>>>
>>>>>>>>>>>> Please check out my patches (inlined elsewhere in this thread), and
>>>>>>>>>>>> comment whether you'd like me to post them to the list as a
>>>>>>>>>>>> standalone series.
>>>>>>>>>>>>
>>>>>>>>>>>> Jian: it wouldn't hurt if you commented as well.
>>>>>>>>>>>>
>>>>>>>>>>>> Thanks
>>>>>>>>>>>> Laszlo
>>>>>>>>>>>>
>>>>>>>>>>>>>> -----Original Message-----
>>>>>>>>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf
>>>> Of
>>>>>>>>>>>> wenyi,xie
>>>>>>>>>>>>>> via groups.io
>>>>>>>>>>>>>> Sent: Friday, August 14, 2020 3:54 PM
>>>>>>>>>>>>>> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io;
>> Yao,
>>>>>>>>>>>>>> Jiewen <jiewen.yao@intel.com>; Wang, Jian J
>>>>>> <jian.j.wang@intel.com>
>>>>>>>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>>>>>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>>>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of
>>>> Offset
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> To Laszlo,
>>>>>>>>>>>>>> Thank you for your detailed description, I agree with what you
>>>>>>>>>>>>>> analyzed and
>>>>>>>>>>>> I'm
>>>>>>>>>>>>>> OK with your patches, it's
>>>>>>>>>>>>>> correct and much simpler.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> To Jiewen,
>>>>>>>>>>>>>> Sorry, I don't have environment to reproduce the issue.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Thanks
>>>>>>>>>>>>>> Wenyi
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> On 2020/8/14 2:50, Laszlo Ersek wrote:
>>>>>>>>>>>>>>> On 08/13/20 13:55, Wenyi Xie wrote:
>>>>>>>>>>>>>>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> There is an integer overflow vulnerability in
>>>>>>>>>>>>>>>> DxeImageVerificationHandler function when parsing the PE
>> files
>>>>>>>>>>>>>>>> attribute certificate table. In cases where
>>>>>>>>>>>>>>>> WinCertificate->dwLength is sufficiently large, it's possible to
>>>>>>>> overflow Offset back to 0 causing an endless loop.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Check offset inbetween VirtualAddress and VirtualAddress +
>> Size.
>>>>>>>>>>>>>>>> Using SafeintLib to do offset addition with result check.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>>>>>>>>>>>>>>>> Cc: Jian J Wang <jian.j.wang@intel.com>
>>>>>>>>>>>>>>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>>> ib.inf
>>>>>>>>>>>> |
>>>>>>>>>>>>>> 1 +
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>>> ib.h
>>>>>>>>>>>> |
>>>>>>>>>>>>>> 1 +
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>> |
>>>>>>>>>>>>>> 111 +++++++++++---------
>>>>>>>>>>>>>>>>  3 files changed, 63 insertions(+), 50 deletions(-)
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>>>
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.inf
>>>>>>>>>>>>>>
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.inf
>>>>>>>>>>>>>>>> index 1e1a639857e0..a7ac4830b3d4 100644
>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.inf
>>>>>>>>>>>>>>>> +++
>>>>>>>>>>>>>>
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.inf
>>>>>>>>>>>>>>>> @@ -53,6 +53,7 @@ [LibraryClasses]
>>>>>>>>>>>>>>>>    SecurityManagementLib
>>>>>>>>>>>>>>>>    PeCoffLib
>>>>>>>>>>>>>>>>    TpmMeasurementLib
>>>>>>>>>>>>>>>> +  SafeIntLib
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>  [Protocols]
>>>>>>>>>>>>>>>>    gEfiFirmwareVolume2ProtocolGuid       ##
>>>>>> SOMETIMES_CONSUMES
>>>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>>>
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.h
>>>>>>>>>>>>>>
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.h
>>>>>>>>>>>>>>>> index 17955ff9774c..060273917d5d 100644
>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.h
>>>>>>>>>>>>>>>> +++
>>>>>>>>>>>>>>
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.h
>>>>>>>>>>>>>>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-
>>>> Patent
>>>>>>>>>>>>>>>> #include <Library/DevicePathLib.h>  #include
>>>>>>>>>>>>>>>> <Library/SecurityManagementLib.h>  #include
>>>> <Library/PeCoffLib.h>
>>>>>>>>>>>>>>>> +#include <Library/SafeIntLib.h>
>>>>>>>>>>>>>>>>  #include <Protocol/FirmwareVolume2.h>  #include
>>>>>>>>>>>>>>>> <Protocol/DevicePath.h>  #include <Protocol/BlockIo.h> diff -
>> -
>>>> git
>>>>>>>>>>>>>>
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>> index 36b87e16d53d..dbc03e28c05b 100644
>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>>>>>> .c
>>>>>>>>>>>>>>>> +++
>>>>>>>>>>>>>>
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>    EFI_STATUS                           HashStatus;
>>>>>>>>>>>>>>>>    EFI_STATUS                           DbStatus;
>>>>>>>>>>>>>>>>    BOOLEAN                              IsFound;
>>>>>>>>>>>>>>>> +  UINT32                               AlignedLength;
>>>>>>>>>>>>>>>> +  UINT32                               Result;
>>>>>>>>>>>>>>>> +  EFI_STATUS                           AddStatus;
>>>>>>>>>>>>>>>> +  BOOLEAN                              IsAuthDataAssigned;
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>    SignatureList     = NULL;
>>>>>>>>>>>>>>>>    SignatureListSize = 0;
>>>>>>>>>>>>>>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
>>>>>>>>>>>>>>>>    IsVerified        = FALSE;
>>>>>>>>>>>>>>>>    IsFound           = FALSE;
>>>>>>>>>>>>>>>> +  Result            = 0;
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>    //
>>>>>>>>>>>>>>>>    // Check the image type and get policy setting.
>>>>>>>>>>>>>>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>    // The first certificate starts at offset
>>>>>>>>>>>>>>>> (SecDataDir->VirtualAddress) from
>>>>>>>>>>>> the
>>>>>>>>>>>>>> start of the file.
>>>>>>>>>>>>>>>>    //
>>>>>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>>>>>>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>> (WinCertificate-
>>>>>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>>>>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet <
>>>>>>>>>>>>>>>> + (SecDataDir-
>>>>>>>>>>>>>>> VirtualAddress + SecDataDir->Size));) {
>>>>>>>>>>>>>>>> +    IsAuthDataAssigned = FALSE;
>>>>>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>> OffSet);
>>>>>>>>>>>>>>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>>>> (WinCertificate-
>>>>>>>>>>>>>>> dwLength);
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> I disagree with this patch.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> The primary reason for my disagreement is that the bug report
>>>>>>>>>>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is
>>>>>>>>>>>>>>> inexact, and so this patch tries to fix the wrong thing.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> With edk2 master at commit 65904cdbb33c, it is *not* possible
>> to
>>>>>>>>>>>>>>> overflow the OffSet variable to zero with "WinCertificate-
>>>>>>> dwLength"
>>>>>>>>>>>>>>> *purely*, and cause an endless loop. Note that we have (at
>>>> commit
>>>>>>>>>>>>>>> 65904cdbb33c):
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>   for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>>>>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>>>>>>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>>>>>>> (WinCertificate-
>>>>>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>>>>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>> OffSet);
>>>>>>>>>>>>>>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>>>>>>>> <= sizeof
>>>>>>>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>>>>>>>>>> WinCertificate-
>>>>>>>>>>>>>>> dwLength) {
>>>>>>>>>>>>>>>       break;
>>>>>>>>>>>>>>>     }
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> The last sub-condition checks whether the Security Data
>> Directory
>>>>>>>>>>>>>>> has enough room left for "WinCertificate->dwLength". If not,
>> then
>>>>>>>>>>>>>>> we break out of the loop.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> If we *do* have enough room, that is:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >=
>>>>>>>>>>>> WinCertificate-
>>>>>>>>>>>>>>> dwLength
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> then we have (by adding OffSet to both sides):
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet +
>>>>>>>>>>>>>>> WinCertificate- dwLength
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> The left hand side is a known-good UINT32, and so
>> incrementing
>>>>>>>>>>>>>>> OffSet (a
>>>>>>>>>>>>>>> UINT32) *solely* by "WinCertificate->dwLength" (also a
>> UINT32)
>>>>>>>>>>>>>>> does not cause an overflow.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Instead, the problem is with the alignment. The "if" statement
>>>>>>>>>>>>>>> checks whether we have enough room for "dwLength", but
>> then
>>>>>>>>>>>>>>> "OffSet" is advanced by "dwLength" *aligned up* to the next
>>>>>>>>>>>>>>> multiple of 8. And that may indeed cause various overflows.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Now, the main problem with the present patch is that it does
>> not
>>>>>>>>>>>>>>> fix one of those overflows. Namely, consider that "dwLength" is
>>>>>>>>>>>>>>> very close to
>>>>>>>>>>>>>>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then
>>>> aligning
>>>>>>>>>>>>>>> it up to the next multiple of 8 will yield 0. In other words,
>>>>>>>> "AlignedLength"
>>>>>>>>>>>>>>> will be zero.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> And when that happens, there's going to be an infinite loop just
>>>>>>>>>>>>>>> the
>>>>>>>>>>>>>>> same: "OffSet" will not be zero, but it will be *stuck*. The
>>>>>>>>>>>>>>> SafeUint32Add() call at the bottom will succeed, but it will not
>>>>>>>>>>>>>>> change the value of "OffSet".
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> More at the bottom.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>>>>>>>>> <= sizeof
>>>>>>>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>>>>>>>>> <
>>>>>>>>>>>>>> WinCertificate->dwLength) {
>>>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>>        AuthData   = PkcsCertData->CertData;
>>>>>>>>>>>>>>>>        AuthDataSize = PkcsCertData->Hdr.dwLength -
>>>>>>>>>>>>>>>> sizeof(PkcsCertData-
>>>>>>>>>>>>> Hdr);
>>>>>>>>>>>>>>>> +      IsAuthDataAssigned = TRUE;
>>>>>>>>>>>>>>>> +      HashStatus = HashPeImageByType (AuthData,
>> AuthDataSize);
>>>>>>>>>>>>>>>>      } else if (WinCertificate->wCertificateType ==
>>>>>>>>>>>> WIN_CERT_TYPE_EFI_GUID)
>>>>>>>>>>>>>> {
>>>>>>>>>>>>>>>>        //
>>>>>>>>>>>>>>>>        // The certificate is formatted as
>>>>>>>>>>>>>>>> WIN_CERTIFICATE_UEFI_GUID which
>>>>>>>>>>>> is
>>>>>>>>>>>>>> described in UEFI Spec.
>>>>>>>>>>>>>>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>        if (WinCertUefiGuid->Hdr.dwLength <=
>>>>>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
>>>>>>>>>>>>>>>>          break;
>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType,
>>>>>>>> &gEfiCertPkcs7Guid))
>>>>>>>>>>>> {
>>>>>>>>>>>>>>>> -        continue;
>>>>>>>>>>>>>>>> +      if (CompareGuid (&WinCertUefiGuid->CertType,
>>>>>>>>>>>>>>>> + &gEfiCertPkcs7Guid))
>>>>>>>>>>>> {
>>>>>>>>>>>>>>>> +        AuthData = WinCertUefiGuid->CertData;
>>>>>>>>>>>>>>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>>>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>>>>>>>>>>> +        IsAuthDataAssigned = TRUE;
>>>>>>>>>>>>>>>> +        HashStatus = HashPeImageByType (AuthData,
>>>> AuthDataSize);
>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>> -      AuthData = WinCertUefiGuid->CertData;
>>>>>>>>>>>>>>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>>>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>>>>>>>>>>>      } else {
>>>>>>>>>>>>>>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE))
>> {
>>>>>>>>>>>>>>>>          break;
>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>> -      continue;
>>>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> -    HashStatus = HashPeImageByType (AuthData,
>> AuthDataSize);
>>>>>>>>>>>>>>>> -    if (EFI_ERROR (HashStatus)) {
>>>>>>>>>>>>>>>> -      continue;
>>>>>>>>>>>>>>>> -    }
>>>>>>>>>>>>>>>> -
>>>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>>>> -    // Check the digital signature against the revoked
>> certificate
>>>> in
>>>>>>>>>>>> forbidden
>>>>>>>>>>>>>> database (dbx).
>>>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>>>>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>>>>>>>>>>>> -      IsVerified = FALSE;
>>>>>>>>>>>>>>>> -      break;
>>>>>>>>>>>>>>>> -    }
>>>>>>>>>>>>>>>> -
>>>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>>>> -    // Check the digital signature against the valid certificate in
>>>>>>>> allowed
>>>>>>>>>>>>>> database (db).
>>>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>>>> -    if (!IsVerified) {
>>>>>>>>>>>>>>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>>>>>>>>>>>> -        IsVerified = TRUE;
>>>>>>>>>>>>>>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
>>>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>>>> +      // Check the digital signature against the revoked
>>>>>>>>>>>>>>>> + certificate in
>>>>>>>>>>>> forbidden
>>>>>>>>>>>>>> database (dbx).
>>>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>>>>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>>>>>>>>>>>> +        IsVerified = FALSE;
>>>>>>>>>>>>>>>> +        break;
>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>> -    }
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>>>> -    // Check the image's hash value.
>>>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>>>> -    DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>>>>>>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>>>>>>>>>>> -                 mImageDigest,
>>>>>>>>>>>>>>>> -                 &mCertType,
>>>>>>>>>>>>>>>> -                 mImageDigestSize,
>>>>>>>>>>>>>>>> -                 &IsFound
>>>>>>>>>>>>>>>> -                 );
>>>>>>>>>>>>>>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>>>>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>>>>>>>>>>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image
>> is
>>>>>>>> signed
>>>>>>>>>>>> but %s
>>>>>>>>>>>>>> hash of image is found in DBX.\n", mHashTypeStr));
>>>>>>>>>>>>>>>> -      IsVerified = FALSE;
>>>>>>>>>>>>>>>> -      break;
>>>>>>>>>>>>>>>> -    }
>>>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>>>> +      // Check the digital signature against the valid
>>>>>>>>>>>>>>>> + certificate in allowed
>>>>>>>>>>>>>> database (db).
>>>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>>>> +      if (!IsVerified) {
>>>>>>>>>>>>>>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>>>>>>>>>>>> +          IsVerified = TRUE;
>>>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>>>> +      }
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> -    if (!IsVerified) {
>>>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>>>> +      // Check the image's hash value.
>>>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>>>>        DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>>>>>>>> -                   EFI_IMAGE_SECURITY_DATABASE,
>>>>>>>>>>>>>>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>>>>>>>>>>>                     mImageDigest,
>>>>>>>>>>>>>>>>                     &mCertType,
>>>>>>>>>>>>>>>>                     mImageDigestSize,
>>>>>>>>>>>>>>>>                     &IsFound
>>>>>>>>>>>>>>>>                     );
>>>>>>>>>>>>>>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>>>>>>>>>>>> -        IsVerified = TRUE;
>>>>>>>>>>>>>>>> -      } else {
>>>>>>>>>>>>>>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image
>> is
>>>>>>>> signed
>>>>>>>>>>>> but
>>>>>>>>>>>>>> signature is not allowed by DB and %s hash of image is not found
>> in
>>>>>>>>>>>> DB/DBX.\n",
>>>>>>>>>>>>>> mHashTypeStr));
>>>>>>>>>>>>>>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>>>>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>>>>>>>>>>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image
>> is
>>>>>>>>>>>>>>>> + signed
>>>>>>>>>>>>>> but %s hash of image is found in DBX.\n", mHashTypeStr));
>>>>>>>>>>>>>>>> +        IsVerified = FALSE;
>>>>>>>>>>>>>>>> +        break;
>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +      if (!IsVerified) {
>>>>>>>>>>>>>>>> +        DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>>>>>>>> +                     EFI_IMAGE_SECURITY_DATABASE,
>>>>>>>>>>>>>>>> +                     mImageDigest,
>>>>>>>>>>>>>>>> +                     &mCertType,
>>>>>>>>>>>>>>>> +                     mImageDigestSize,
>>>>>>>>>>>>>>>> +                     &IsFound
>>>>>>>>>>>>>>>> +                     );
>>>>>>>>>>>>>>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>>>>>>>>>>>> +          IsVerified = TRUE;
>>>>>>>>>>>>>>>> +        } else {
>>>>>>>>>>>>>>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib:
>> Image
>>>> is
>>>>>>>>>>>>>>>> + signed
>>>>>>>>>>>> but
>>>>>>>>>>>>>> signature is not allowed by DB and %s hash of image is not found
>> in
>>>>>>>>>>>> DB/DBX.\n",
>>>>>>>>>>>>>> mHashTypeStr));
>>>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>>>> +      }
>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength,
>> &Result);
>>>>>>>>>>>>>>>> +    if (EFI_ERROR (AddStatus)) {
>>>>>>>>>>>>>>>> +      break;
>>>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>>> +    OffSet = Result;
>>>>>>>>>>>>>>>>    }
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
>>>>>>>>>>>>>>>> {
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> There are other (smaller) reasons why I dislike this patch:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> - The "IsAuthDataAssigned" variable is superfluous; we could
>> use
>>>>>>>>>>>>>>> the existent "AuthData" variable (with a NULL-check and a
>>>>>>>>>>>>>>> NULL-assignment) similarly.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> - The patch complicates / reorganizes the control flow
>> needlessly.
>>>>>>>>>>>>>>> This complication originates from placing the checked "OffSet"
>>>>>>>>>>>>>>> increment at the bottom of the loop, which then requires the
>>>>>>>>>>>>>>> removal of all the "continue" statements. But we don't need to
>>>>>>>>>>>>>>> check-and-increment at the bottom. We can keep the
>> increment
>>>>>>>>>>>>>>> inside the "for" statement, only extend the *existent* room
>> check
>>>>>>>>>>>>>>> (which I've quoted) to take the alignment into account as well.
>> If
>>>>>>>>>>>>>>> there is enough room for the alignment in the security data
>>>>>>>>>>>>>>> directory, then that guarantees there won't be a UINT32
>> overflow
>>>>>>>> either.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> All in all, I'm proposing the following three patches instead. The
>>>>>>>>>>>>>>> first two patches are preparation, the last patch is the fix.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Patch#1:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon
>> Sep
>>>> 17
>>>>>>>>>>>> 00:00:00
>>>>>>>>>>>>>> 2001
>>>>>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
>>>>>>>>>>>>>>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib:
>>>> extract
>>>>>>>>>>>>>>>> SecDataDirEnd, SecDataDirLeft
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> The following two quantities:
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> are used multiple times in DxeImageVerificationHandler().
>>>>>>>>>>>>>>>> Introduce helper variables for them: "SecDataDirEnd" and
>>>>>>>> "SecDataDirLeft", respectively.
>>>>>>>>>>>>>>>> This saves us multiple calculations and significantly simplifies
>> the
>>>>>> code.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Note that all three summands above have type UINT32,
>>>> therefore
>>>>>>>>>>>>>>>> the new variables are also of type UINT32.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> This patch does not change behavior.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> (Note that the code already handles the case when the
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> UINT32 addition overflows -- namely, in that case, the
>>>>>>>>>>>>>>>> certificate loop is never entered, and the corruption check
>> right
>>>>>>>>>>>>>>>> after the loop fires.)
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>>> ib.c |
>>>>>>>>>>>> 12
>>>>>>>>>>>>>> ++++++++----
>>>>>>>>>>>>>>>>  1 file changed, 8 insertions(+), 4 deletions(-)
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>>>
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>> index 36b87e16d53d..8761980c88aa 100644
>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>>>>>> .c
>>>>>>>>>>>>>>>> +++
>>>>>>>>>>>>>>
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>    UINT8                                *AuthData;
>>>>>>>>>>>>>>>>    UINTN                                AuthDataSize;
>>>>>>>>>>>>>>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
>>>>>>>>>>>>>>>> +  UINT32                               SecDataDirEnd;
>>>>>>>>>>>>>>>> +  UINT32                               SecDataDirLeft;
>>>>>>>>>>>>>>>>    UINT32                               OffSet;
>>>>>>>>>>>>>>>>    CHAR16                               *NameStr;
>>>>>>>>>>>>>>>>    RETURN_STATUS                        PeCoffStatus;
>>>>>>>>>>>>>>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>    // "Attribute Certificate Table".
>>>>>>>>>>>>>>>>    // The first certificate starts at offset
>>>>>>>>>>>>>>>> (SecDataDir->VirtualAddress) from
>>>>>>>>>>>> the
>>>>>>>>>>>>>> start of the file.
>>>>>>>>>>>>>>>>    //
>>>>>>>>>>>>>>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir-
>>>>> Size;
>>>>>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>>>>>>>> +       OffSet < SecDataDirEnd;
>>>>>>>>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>>>>>>>> (WinCertificate-
>>>>>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>> OffSet);
>>>>>>>>>>>>>>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>> <=
>>>>>>>> sizeof
>>>>>>>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>> <
>>>>>>>>>>>>>> WinCertificate->dwLength) {
>>>>>>>>>>>>>>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>>>>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>>>> +        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>>>    }
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
>>>>>>>>>>>>>>>> {
>>>>>>>>>>>>>>>> +  if (OffSet != SecDataDirEnd) {
>>>>>>>>>>>>>>>>      //
>>>>>>>>>>>>>>>>      // The Size in Certificate Table or the attribute
>>>>>>>>>>>>>>>> certificate table is
>>>>>>>>>>>> corrupted.
>>>>>>>>>>>>>>>>      //
>>>>>>>>>>>>>>>> --
>>>>>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Patch#2:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep
>>>> 17
>>>>>>>> 00:00:00
>>>>>>>>>>>>>> 2001
>>>>>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
>>>>>>>>>>>>>>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib:
>>>> assign
>>>>>>>>>>>>>>>> WinCertificate after size check
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE))
>>>> check
>>>>>>>>>>>>>>>> only guards the de-referencing of the "WinCertificate" pointer.
>>>>>>>>>>>>>>>> It does not guard the calculation of hte pointer itself:
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>> OffSet);
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> This is wrong; if we don't know for sure that we have enough
>>>> room
>>>>>>>>>>>>>>>> for a WIN_CERTIFICATE, then even creating such a pointer,
>> not
>>>>>>>>>>>>>>>> just de-referencing it, may invoke undefined behavior.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Move the pointer calculation after the size check.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>>> ib.c |
>>>>>>>>>>>> 8
>>>>>>>>>>>>>> +++++---
>>>>>>>>>>>>>>>>  1 file changed, 5 insertions(+), 3 deletions(-)
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>>>
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>> index 8761980c88aa..461ed7cfb5ac 100644
>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>>>>>> .c
>>>>>>>>>>>>>>>> +++
>>>>>>>>>>>>>>
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>>>>>>         OffSet < SecDataDirEnd;
>>>>>>>>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>>>>>>>> (WinCertificate-
>>>>>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>>>>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>>>> OffSet);
>>>>>>>>>>>>>>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>>>>>>>>>>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>>>> -        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
>>>>>>>>>>>>>>>> +      break;
>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>>>> OffSet);
>>>>>>>>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> --
>>>>>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Patch#3:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep
>> 17
>>>>>>>> 00:00:00
>>>>>>>>>>>>>> 2001
>>>>>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
>>>>>>>>>>>>>>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib:
>> catch
>>>>>>>>>>>> alignment
>>>>>>>>>>>>>>>>  overflow (CVE-2019-14562)
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> The DxeImageVerificationHandler() function currently checks
>>>>>>>>>>>>>>>> whether "SecDataDir" has enough room for
>>>>>>>>>>>>>>>> "WinCertificate->dwLength". However,
>>>>>>>>>>>>>> for
>>>>>>>>>>>>>>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to
>> the
>>>>>>>>>>>>>>>> next multiple of 8. If "WinCertificate->dwLength" is large
>>>>>>>>>>>>>>>> enough, the alignment will return 0, and "OffSet" will be stuck
>> at
>>>>>> the
>>>>>>>> same value.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Check whether "SecDataDir" has room left for both
>>>>>>>>>>>>>>>> "WinCertificate->dwLength" and the alignment.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>>> ib.c |
>>>>>>>>>>>> 4
>>>>>>>>>>>>>> +++-
>>>>>>>>>>>>>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>>>
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>> index 461ed7cfb5ac..e38eb981b7a0 100644
>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>
>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>>>>>> .c
>>>>>>>>>>>>>>>> +++
>>>>>>>>>>>>>>
>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>> OffSet);
>>>>>>>>>>>>>>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
>>>>>>>>>>>>>>>> +        (SecDataDirLeft - WinCertificate->dwLength <
>>>>>>>>>>>>>>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
>>>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> --
>>>>>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> If Wenyi and the reviewers are OK with these patches, I can
>>>> submit
>>>>>>>>>>>>>>> them as a standalone patch series.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Note that I do not have any reproducer for the issue; the best
>>>>>>>>>>>>>>> testing that I could offer would be some light-weight Secure
>> Boot
>>>>>>>>>>>>>>> regression tests.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Thanks
>>>>>>>>>>>>>>> Laszlo
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> .
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> .
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>
>>>>
>>>>
>>>
>>
>>
>> 
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [edk2-devel] [PATCH EDK2 v2 1/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
  2020-09-01  7:43                                   ` wenyi,xie
@ 2020-09-01  7:56                                     ` Laszlo Ersek
  0 siblings, 0 replies; 32+ messages in thread
From: Laszlo Ersek @ 2020-09-01  7:56 UTC (permalink / raw)
  To: xiewenyi (A), Yao, Jiewen, devel@edk2.groups.io, Wang, Jian J
  Cc: songdongkuang@huawei.com, Mathews, John

On 09/01/20 09:43, xiewenyi (A) wrote:
> To Jiewen,
> 
> Sorry to make you lost,I mean the patches Laszlo proposed in email,
> https://edk2.groups.io/g/devel/message/64243
> http://mid.mail-archive.com/eb0c6bcb-77fb-2fb9-783e-aa5025953a80@redhat.com
> 
> To Laszlo,
> 
> Sure, I will test your patches against my reproducer and add "tested-by:" tag to the patches after you post them.

Thank you!, I'll send them soon.

Cheers!
Laszlo

> 
> Regards
> Wenyi
> 
> On 2020/9/1 15:31, Yao, Jiewen wrote:
>> I am sorry, that I am a little lost here.
>>
>> We have discussed different patches. I am not 100% sure which one is "Laszlo's patches".
>>
>> To make thing easy and record all actions, would you please reply the patch(es) you have verified, with your "tested-by:" tag?
>>
>> Thank you
>> Yao Jiewen
>>
>>> -----Original Message-----
>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of wenyi,xie
>>> via groups.io
>>> Sent: Tuesday, September 1, 2020 3:11 PM
>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Laszlo Ersek
>>> <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>
>>> Cc: songdongkuang@huawei.com; Mathews, John <john.mathews@intel.com>
>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>
>>> I think Laszlo's patches is OK, I have applied and tested it using my case. It can
>>> catch the issue.
>>> DEBUG code and log below,
>>>
>>>   SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size;
>>>   DEBUG((DEBUG_INFO, "SecDataDirEnd=0x%x.\n", SecDataDirEnd));
>>>   for (OffSet = SecDataDir->VirtualAddress;
>>>        OffSet < SecDataDirEnd;
>>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>>> dwLength))) {
>>>     SecDataDirLeft = SecDataDirEnd - OffSet;
>>>     DEBUG((DEBUG_INFO, "OffSet=0x%x, SecDataDirLeft=0x%x.\n", OffSet,
>>> SecDataDirLeft));
>>>     if (SecDataDirLeft <= sizeof(WIN_CERTIFICATE)) {
>>>       break;
>>>     }
>>>     WinCertificate = (WIN_CERTIFICATE *)(mImageBase + OffSet);
>>>     DEBUG((DEBUG_INFO, "WinCertificate->dwLength=0x%x, ALIGN_SIZE
>>> (WinCertificate->dwLength)=0x%x.\n", WinCertificate->dwLength,
>>> ALIGN_SIZE(WinCertificate->dwLength)));
>>>     if (SecDataDirLeft < WinCertificate->dwLength ||
>>> 	(SecDataDirLeft - WinCertificate->dwLength <
>>> ALIGN_SIZE(WinCertificate->dwLength))) {
>>>       DEBUG((DEBUG_INFO, "Parameter is invalid and break.\n"));
>>>       break;
>>>     }
>>>
>>> SecDataDirEnd=0xFFFFFFFC.
>>> OffSet=0xE000, SecDataDirLeft=0xFFFF1FFC.
>>> WinCertificate->dwLength=0xFFFF1FFB, ALIGN_SIZE (WinCertificate-
>>>> dwLength)=0x5.
>>> Parameter is invalid and break.
>>> The image doesn't pass verification: VenHw(5CF32E0B-8EDF-2E44-9CDA-
>>> 93205E99EC1C,00000000)/VenHw(964E5B22-6459-11D2-8E39-
>>> 00A0C969723B,00000000)/\signed_1234_4G.efi
>>>
>>> Regards
>>> Wenyi
>>>
>>> On 2020/9/1 0:06, Yao, Jiewen wrote:
>>>> Sounds great. Appreciate your hard work on that.
>>>>
>>>> Will you post a patch to fix the issue again?
>>>>
>>>> Thank you
>>>> Yao Jiewen
>>>>
>>>>> -----Original Message-----
>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>>> wenyi,xie
>>>>> via groups.io
>>>>> Sent: Monday, August 31, 2020 7:24 PM
>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Laszlo
>>> Ersek
>>>>> <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>
>>>>> Cc: songdongkuang@huawei.com; Mathews, John
>>> <john.mathews@intel.com>
>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>
>>>>> Hi,Jiewen,
>>>>>
>>>>> I modify the PE file again, this time it can pass the check in PeCoffLib and
>>> cause
>>>>> offset overflow.
>>>>>
>>>>> First, create a PE file and sign it(only one signature), then using binary edit
>>> tool
>>>>> to modify content of PE file like below,
>>>>>  1.check the value of SecDataDir->VirtualAddress, in my PE file, it's 0xE000
>>>>>  2.changing SecDataDir->Size from 0x5F8 to 0xFFFF1FFC
>>>>>  3.changing WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFB
>>>>>  4.padding PE file with 0 until the size of the file is 0xFFFFFFFC(it will make the
>>> PE
>>>>> file so large)
>>>>> OffSet + WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength)
>>> is
>>>>> 0xE000 + 0xFFFF1FFB + 0x5 = 0x100000000
>>>>>
>>>>> Below is the DEBUG code and log, in second loop the offset overflow and
>>>>> become 0
>>>>>
>>>>> for (OffSet = SecDataDir->VirtualAddress;
>>>>>      OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>      OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>>>>> dwLength))) {
>>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
>>>>>   DEBUG((DEBUG_INFO, "OffSet=0x%x.\n", OffSet));
>>>>>   if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>>>>> (WIN_CERTIFICATE) ||
>>>>>       (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate-
>>>>>> dwLength) {
>>>>>     break;
>>>>>   }
>>>>>   DEBUG((DEBUG_INFO, "WinCertificate->dwLength=0x%x, ALIGN_SIZE
>>>>> (WinCertificate->dwLength)=0x%x.\n", WinCertificate->dwLength,
>>>>> ALIGN_SIZE(WinCertificate->dwLength)));
>>>>>
>>>>>
>>>>> SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFC.
>>>>> OffSet=0xE000.
>>>>> WinCertificate->dwLength=0xFFFF1FFB, ALIGN_SIZE (WinCertificate-
>>>>>> dwLength)=0x5.
>>>>> DxeImageVerificationLib: Image is signed but signature is not allowed by DB
>>> and
>>>>> SHA256 hash of image is notOffSet=0x0.
>>>>> WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate-
>>>>>> dwLength)=0x3.
>>>>> OffSet=0x5A50.
>>>>> WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate-
>>>>>> dwLength)=0x4.
>>>>> OffSet=0xEA78.
>>>>> WinCertificate->dwLength=0x0, ALIGN_SIZE (WinCertificate->dwLength)=0x0.
>>>>> The image doesn't pass verification: VenHw(5CF32E0B-8EDF-2E44-9CDA-
>>>>> 93205E99EC1C,00000000)/VenHw(964E5B22-6459-11D2-8E39-
>>>>> 00A0C969723B,00000000)/\signed_1234_4G.efi
>>>>>
>>>>>
>>>>> Regards
>>>>> Wenyi
>>>>>
>>>>>
>>>>> On 2020/8/28 14:43, Yao, Jiewen wrote:
>>>>>> Apology that I did not say clearly.
>>>>>> I mean you should not modify any code to perform an attack.
>>>>>>
>>>>>> I am not asking you to exploit the system. Attack here just means: to cause
>>>>> system hang or buffer overflow. That is enough.
>>>>>> But you cannot modify code to remove any existing checker.
>>>>>>
>>>>>> Thank you
>>>>>> Yao Jiewen
>>>>>>
>>>>>>> -----Original Message-----
>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>>>>> wenyi,xie
>>>>>>> via groups.io
>>>>>>> Sent: Friday, August 28, 2020 2:18 PM
>>>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io; Laszlo
>>>>> Ersek
>>>>>>> <lersek@redhat.com>; Wang, Jian J <jian.j.wang@intel.com>
>>>>>>> Cc: songdongkuang@huawei.com; Mathews, John
>>>>> <john.mathews@intel.com>
>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>>
>>>>>>> Hi,Jiewen,
>>>>>>>
>>>>>>> I don't really get the meaning "create a case that bypass all checks in
>>>>> PeCoffLib",
>>>>>>> do you mean I need to create a PE file that can bypass all check in
>>> PeCoffLib
>>>>>>> without modify any
>>>>>>> code and then cause the problem in DxeImageVerificationLib, or just
>>> modify
>>>>>>> some code in PeCoffLib to bypass check instead of removing the calling of
>>>>>>> PeCoffLoaderGetImageInfo. Would
>>>>>>> you mind explaining a little more specifically? As far as I tried, it's really
>>> hard
>>>>> to
>>>>>>> reproduce the issue without touching any code.
>>>>>>>
>>>>>>> Thanks
>>>>>>> Wenyi
>>>>>>>
>>>>>>> On 2020/8/28 11:50, Yao, Jiewen wrote:
>>>>>>>> HI Wenyi
>>>>>>>> Thank you very much to take time to reproduce.
>>>>>>>>
>>>>>>>> I am particular interested in below:
>>>>>>>> 	"As PE file is modified, function PeCoffLoaderGetImageInfo will return
>>>>>>> error, so I have to remove it so that for loop can be tested in
>>>>>>> DxeImageVerificationHandler."
>>>>>>>>
>>>>>>>> By design, the PeCoffLib should catch illegal PE/COFF image and return
>>> error.
>>>>>>> (even it cannot catch all, it should catch most ones).
>>>>>>>> Other PE/COFF parser may rely on the checker in PeCoffLib and *no
>>> need*
>>>>> to
>>>>>>> duplicate all checkers.
>>>>>>>> As such, DxeImageVerificationLib (and other PeCoff consumer) just need
>>>>>>> checks what has passed the check in PeCoffLib.
>>>>>>>>
>>>>>>>> I don’t think you should remove the checker. If people can remove the
>>>>> checker,
>>>>>>> I am sure the rest code will be vulnerable, according to the current design.
>>>>>>>> Could you please to create a case that bypass all checks in PeCoffLib,
>>> then
>>>>> run
>>>>>>> into DxeImageVerificationLib and cause the problem? That would be more
>>>>>>> valuable for us.
>>>>>>>>
>>>>>>>> Thank you
>>>>>>>> Yao Jiewen
>>>>>>>>
>>>>>>>>> -----Original Message-----
>>>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
>>>>>>> wenyi,xie
>>>>>>>>> via groups.io
>>>>>>>>> Sent: Friday, August 28, 2020 11:18 AM
>>>>>>>>> To: Laszlo Ersek <lersek@redhat.com>; Wang, Jian J
>>>>>>> <jian.j.wang@intel.com>;
>>>>>>>>> devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>
>>>>>>>>> Cc: songdongkuang@huawei.com; Mathews, John
>>>>>>> <john.mathews@intel.com>
>>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>>>>
>>>>>>>>> Hi,Laszlo and everyone,
>>>>>>>>>
>>>>>>>>> These days I tried to reproduce the issue,and made some progress. I
>>>>> think
>>>>>>>>> there are two way to cause overflow from a mathematical point of view,
>>>>>>>>> 1.As Laszlo analysed before, if WinCertificate->dwLength is large
>>> enough,
>>>>>>> close
>>>>>>>>> to MAX_UINT32, then (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>> (WinCertificate-
>>>>>>>>>> dwLength)) will cause overflow.
>>>>>>>>> 2.(WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))
>>> is
>>>>>>> good,
>>>>>>>>> OffSet is good, but OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>> (WinCertificate->dwLength)) cause overflow.
>>>>>>>>>
>>>>>>>>> Here I choose the second way to reproduce the issue, I choose a PE file
>>>>> and
>>>>>>> sign
>>>>>>>>> it with my own db certificate. Then I use binary edit tool to modify the
>>> PE
>>>>> file
>>>>>>> like
>>>>>>>>> below,
>>>>>>>>>
>>>>>>>>> 1.change SecDataDir->Size from 0x5F8 to 0xFFFF1FFF
>>>>>>>>> 2.change WinCertificate->dwLength from 0x5F1 to 0xFFFF1FFE
>>>>>>>>> SecDataDir->VirtualAddress in my PE is 0xe000 and no need to change.
>>>>>>>>>
>>>>>>>>> As PE file is modified, function PeCoffLoaderGetImageInfo will return
>>> error,
>>>>>>> so I
>>>>>>>>> have to remove it so that for loop can be tested in
>>>>>>> DxeImageVerificationHandler.
>>>>>>>>> The log is as below,
>>>>>>>>>
>>>>>>>>> SecDataDir->VirtualAddress=0xE000, SecDataDir->Size=0xFFFF1FFF.
>>>>>>>>> (First Loop)
>>>>>>>>> OffSet=0xE000.
>>>>>>>>> WinCertificate->dwLength=0xFFFF1FFE, ALIGN_SIZE (WinCertificate-
>>>>>>>>>> dwLength)=0x2.
>>>>>>>>> (Second Loop)
>>>>>>>>> OffSet=0x0.
>>>>>>>>> WinCertificate->dwLength=0x5A4D, ALIGN_SIZE (WinCertificate-
>>>>>>>>>> dwLength)=0x3.
>>>>>>>>> (Third Loop)
>>>>>>>>> OffSet=0x5A50.
>>>>>>>>> WinCertificate->dwLength=0x9024, ALIGN_SIZE (WinCertificate-
>>>>>>>>>> dwLength)=0x4.
>>>>>>>>> (Forth Loop)
>>>>>>>>> OffSet=0xEA78.
>>>>>>>>> WinCertificate->dwLength=0xAFAFAFAF, ALIGN_SIZE (WinCertificate-
>>>>>>>>>> dwLength)=0x1.
>>>>>>>>> (Fifth Loop)
>>>>>>>>> OffSet=0xAFB09A28.
>>>>>>>>>
>>>>>>>>> As I modify SecDataDir->Size and WinCertificate->dwLength, so in first
>>>>> loop
>>>>>>> the
>>>>>>>>> condition check whether the Security Data Directory has enough room
>>> left
>>>>>>> for
>>>>>>>>> "WinCertificate->dwLength" is ok.
>>>>>>>>>
>>>>>>>>> if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof
>>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>     (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>>> WinCertificate-
>>>>>>>>>> dwLength) {
>>>>>>>>>   break;
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>> In the beginning of second loop, WinCertificate->dwLength +
>>> ALIGN_SIZE
>>>>>>>>> (WinCertificate->dwLength) is 0xFFFF2000, offset is 0xE000
>>>>>>>>>
>>>>>>>>> OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate-
>>>>>>>> dwLength))
>>>>>>>>>
>>>>>>>>> Offset now is 0 and overflow happens. So even if my PE only have one
>>>>>>> signature,
>>>>>>>>> the for loop is still going untill exception happens.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> I can't reproduce the issue using the first way, because if WinCertificate-
>>>>>>>>>> dwLength is close to MAX_UINT32, it means SecDataDir->Size should
>>> also
>>>>>>> close
>>>>>>>>> to MAX_UINT32, or the condition check
>>>>>>>>> "(SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>>> WinCertificate-
>>>>>>>>>> dwLength" will break. But if SecDataDir->Size is very large, SecDataDir-
>>>>>>>>>> VirtualAddress have to be smaller than 8 bytes,
>>>>>>>>> because SecDataDir->VirtualAddress + SecDataDir->Size < MAX_UINT32.
>>>>>>>>> SecDataDir->VirtualAddress is the beginning address of the signature,
>>> and
>>>>>>> before
>>>>>>>>> SecDataDir->VirtualAddress is the content of origin PE file, I think it's
>>>>>>> impossible
>>>>>>>>> that the size of PE file is only 8 bytes.
>>>>>>>>>
>>>>>>>>> As I changed the one line code in DxeImageVerificationHandler to
>>>>> reproduce
>>>>>>> the
>>>>>>>>> issue, I'm not sure whether it's ok.
>>>>>>>>>
>>>>>>>>> Thanks
>>>>>>>>> Wenyi
>>>>>>>>>
>>>>>>>>> On 2020/8/19 17:26, Laszlo Ersek wrote:
>>>>>>>>>> On 08/18/20 17:18, Mathews, John wrote:
>>>>>>>>>>> I dug up the original report details.  This was noted as a concern
>>> during a
>>>>>>>>> source code inspection.  There was no demonstration of how it might be
>>>>>>>>> triggered.
>>>>>>>>>>>
>>>>>>>>>>> " There is an integer overflow vulnerability in the
>>>>>>>>> DxeImageVerificationHandler function when
>>>>>>>>>>> parsing the PE files attribute certificate table. In cases where
>>>>>>> WinCertificate-
>>>>>>>>>> dwLength is
>>>>>>>>>>> sufficiently large, it's possible to overflow Offset back to 0 causing an
>>>>>>> endless
>>>>>>>>> loop."
>>>>>>>>>>>
>>>>>>>>>>> The recommendation was to add stricter checking of "Offset" and the
>>>>>>>>> embedded length fields of certificate data
>>>>>>>>>>> before using them.
>>>>>>>>>>
>>>>>>>>>> Thanks for checking!
>>>>>>>>>>
>>>>>>>>>> Laszlo
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> -----Original Message-----
>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>> Sent: Tuesday, August 18, 2020 1:59 AM
>>>>>>>>>>> To: Wang, Jian J <jian.j.wang@intel.com>; devel@edk2.groups.io;
>>> Yao,
>>>>>>>>> Jiewen <jiewen.yao@intel.com>; xiewenyi2@huawei.com
>>>>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com;
>>>>> Mathews,
>>>>>>>>> John <john.mathews@intel.com>
>>>>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset
>>>>>>>>>>>
>>>>>>>>>>> On 08/18/20 04:10, Wang, Jian J wrote:
>>>>>>>>>>>> Laszlo,
>>>>>>>>>>>>
>>>>>>>>>>>> My apologies for the slow response. I'm not the original reporter but
>>>>>>>>>>>> just the BZ submitter. And I didn't do deep analysis to this issue.
>>>>>>>>>>>> The issues was reported from one internal team. Add John in loop to
>>>>> see
>>>>>>> if
>>>>>>>>> he knows more about it or not.
>>>>>>>>>>>>
>>>>>>>>>>>> My superficial understanding on such issue is that, if there's
>>>>>>>>>>>> "potential" issue in theory and hard to reproduce, it's still worthy
>>>>>>>>>>>> of using an alternative way to replace the original implementation
>>>>>>>>>>>> with no "potential" issue at all. Maybe we don't have to prove old
>>> way
>>>>> is
>>>>>>>>> something wrong but must prove that the new way is really safe.
>>>>>>>>>>>
>>>>>>>>>>> I agree, thanks.
>>>>>>>>>>>
>>>>>>>>>>> It would be nice to hear more from the internal team about the
>>>>> originally
>>>>>>>>> reported (even if hard-to-trigger) issue.
>>>>>>>>>>>
>>>>>>>>>>> Thanks!
>>>>>>>>>>> Laszlo
>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Regards,
>>>>>>>>>>>> Jian
>>>>>>>>>>>>
>>>>>>>>>>>>> -----Original Message-----
>>>>>>>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf
>>> Of
>>>>>>> Laszlo
>>>>>>>>>>>>> Ersek
>>>>>>>>>>>>> Sent: Tuesday, August 18, 2020 12:53 AM
>>>>>>>>>>>>> To: Yao, Jiewen <jiewen.yao@intel.com>; devel@edk2.groups.io;
>>>>>>>>>>>>> xiewenyi2@huawei.com; Wang, Jian J <jian.j.wang@intel.com>
>>>>>>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>>>>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of
>>> Offset
>>>>>>>>>>>>>
>>>>>>>>>>>>> Hi Jiewen,
>>>>>>>>>>>>>
>>>>>>>>>>>>> On 08/14/20 10:53, Yao, Jiewen wrote:
>>>>>>>>>>>>>>> To Jiewen,
>>>>>>>>>>>>>>> Sorry, I don't have environment to reproduce the issue.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Please help me understand, if you don’t have environment to
>>>>>>>>>>>>>> reproduce the
>>>>>>>>>>>>> issue, how do you guarantee that your patch does fix the problem
>>> and
>>>>>>>>>>>>> we don’t have any other vulnerabilities?
>>>>>>>>>>>>>
>>>>>>>>>>>>> The original bug report in
>>>>>>>>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is
>>>>> seriously
>>>>>>>>>>>>> lacking. It does not go into detail about the alleged integer
>>> overflow.
>>>>>>>>>>>>> It does not quote the code, does not explain the control flow, does
>>>>>>>>>>>>> not identify the exact edk2 commit at which the vulnerability exists.
>>>>>>>>>>>>>
>>>>>>>>>>>>> The bug report also does not offer a reproducer.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Additionally, the exact statement that the bug report does make,
>>>>>>>>>>>>> namely
>>>>>>>>>>>>>
>>>>>>>>>>>>>   it's possible to overflow Offset back to 0 causing an endless loop
>>>>>>>>>>>>>
>>>>>>>>>>>>> is wrong (as far as I can tell anyway). It is not "OffSet" that can
>>>>>>>>>>>>> be overflowed to zero, but the *addend* that is added to OffSet
>>> can
>>>>>>>>>>>>> be overflowed to zero. Therefore the infinite loop will arise
>>> because
>>>>>>>>>>>>> OffSet remains stuck at its present value, and not because OffSet
>>>>>>>>>>>>> will be re-set to zero.
>>>>>>>>>>>>>
>>>>>>>>>>>>> For the reasons, we can only speculate as to what the actual
>>> problem
>>>>>>>>>>>>> is, unless Jian decides to join the discussion and clarifies what he
>>>>>>>>>>>>> had in mind originally.
>>>>>>>>>>>>>
>>>>>>>>>>>>> My understanding (or even "reconstruction") of the vulnerability is
>>>>>>>>>>>>> described above, and in the patches that I proposed.
>>>>>>>>>>>>>
>>>>>>>>>>>>> We can write a patch based on code analysis. It's possible to
>>>>>>>>>>>>> identify integer overflows based on code analysis, and it's possible
>>>>>>>>>>>>> to verify the correctness of fixes by code review. Obviously testing
>>>>>>>>>>>>> is always good, but many times, constructing reproducers for such
>>>>>>>>>>>>> issues that were found by code review, is difficult and time
>>>>>>>>>>>>> consuming. We can say that we don't fix vulnerabilities without
>>>>>>>>>>>>> reproducers, or we can say that we make an effort to fix them even
>>> if
>>>>>>>>>>>>> all we have is code analysis (and not a reproducer).
>>>>>>>>>>>>>
>>>>>>>>>>>>> So the above paragraph concerns "correctness". Regarding
>>>>>>>>>>>>> "completeness", I guarantee you that this patch does not fix *all*
>>>>>>>>>>>>> problems related to PE parsing. (See the other BZ tickets.) It does
>>>>>>>>>>>>> fix *one* issue with PE parsing. We can say that we try to fix such
>>>>>>>>>>>>> issues gradually (give different CVE numbers to different issues, and
>>>>>>>>>>>>> address them one at a time), or we can say that we rewrite PE
>>> parsing
>>>>>>>>> from the ground up.
>>>>>>>>>>>>> (BTW: I have seriously attempted that in the past, and I gave up,
>>>>>>>>>>>>> because the PE format is FUBAR.)
>>>>>>>>>>>>>
>>>>>>>>>>>>> In summary:
>>>>>>>>>>>>>
>>>>>>>>>>>>> - the problem statement is unclear,
>>>>>>>>>>>>>
>>>>>>>>>>>>> - it seems like there is indeed an integer overflow problem in the
>>>>>>>>>>>>> SecDataDir parsing loop, but it's uncertain whether the bug
>>> reporter
>>>>>>>>>>>>> had exactly that in mind
>>>>>>>>>>>>>
>>>>>>>>>>>>> - PE parsing is guaranteed to have other vulnerabilities elsewhere in
>>>>>>>>>>>>> edk2, but I'm currently unaware of other such issues in
>>>>>>>>>>>>> DxeImageVerificationLib specifically
>>>>>>>>>>>>>
>>>>>>>>>>>>> - even if there are other such problems (in DxeImageVerificationLib
>>>>>>>>>>>>> or elswehere), fixing this bug that we know about is likely
>>>>>>>>>>>>> worthwhile
>>>>>>>>>>>>>
>>>>>>>>>>>>> - for many such bugs, constructing a reproducer is difficult and time
>>>>>>>>>>>>> consuming; code analysis, and *regression-testing* are frequently
>>> the
>>>>>>>>>>>>> only tools we have. That doesn't mean we should ignore this class
>>> of
>>>>>>> bugs.
>>>>>>>>>>>>>
>>>>>>>>>>>>> (Fixing integer overflows retro-actively is more difficult than
>>>>>>>>>>>>> writing overflow-free code in the first place, but that ship has
>>>>>>>>>>>>> sailed; so we can only fight these bugs incrementally now, unless
>>> we
>>>>>>>>>>>>> can rewrite PE parsing with a new data structure from the ground
>>> up.
>>>>>>>>>>>>> Again I tried that and gave up, because the spec is not public, and
>>>>>>>>>>>>> what I did manage to learn about PE, showed that it was insanely
>>>>>>>>>>>>> over-engineered. I'm not saying that other binary / executable
>>>>>>>>>>>>> formats are better, of course.)
>>>>>>>>>>>>>
>>>>>>>>>>>>> Please check out my patches (inlined elsewhere in this thread), and
>>>>>>>>>>>>> comment whether you'd like me to post them to the list as a
>>>>>>>>>>>>> standalone series.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Jian: it wouldn't hurt if you commented as well.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Thanks
>>>>>>>>>>>>> Laszlo
>>>>>>>>>>>>>
>>>>>>>>>>>>>>> -----Original Message-----
>>>>>>>>>>>>>>> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf
>>>>> Of
>>>>>>>>>>>>> wenyi,xie
>>>>>>>>>>>>>>> via groups.io
>>>>>>>>>>>>>>> Sent: Friday, August 14, 2020 3:54 PM
>>>>>>>>>>>>>>> To: Laszlo Ersek <lersek@redhat.com>; devel@edk2.groups.io;
>>> Yao,
>>>>>>>>>>>>>>> Jiewen <jiewen.yao@intel.com>; Wang, Jian J
>>>>>>> <jian.j.wang@intel.com>
>>>>>>>>>>>>>>> Cc: huangming23@huawei.com; songdongkuang@huawei.com
>>>>>>>>>>>>>>> Subject: Re: [edk2-devel] [PATCH EDK2 v2 1/1]
>>>>>>>>>>>>>>> SecurityPkg/DxeImageVerificationLib:Enhanced verification of
>>>>> Offset
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> To Laszlo,
>>>>>>>>>>>>>>> Thank you for your detailed description, I agree with what you
>>>>>>>>>>>>>>> analyzed and
>>>>>>>>>>>>> I'm
>>>>>>>>>>>>>>> OK with your patches, it's
>>>>>>>>>>>>>>> correct and much simpler.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> To Jiewen,
>>>>>>>>>>>>>>> Sorry, I don't have environment to reproduce the issue.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Thanks
>>>>>>>>>>>>>>> Wenyi
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> On 2020/8/14 2:50, Laszlo Ersek wrote:
>>>>>>>>>>>>>>>> On 08/13/20 13:55, Wenyi Xie wrote:
>>>>>>>>>>>>>>>>> REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2215
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> There is an integer overflow vulnerability in
>>>>>>>>>>>>>>>>> DxeImageVerificationHandler function when parsing the PE
>>> files
>>>>>>>>>>>>>>>>> attribute certificate table. In cases where
>>>>>>>>>>>>>>>>> WinCertificate->dwLength is sufficiently large, it's possible to
>>>>>>>>> overflow Offset back to 0 causing an endless loop.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Check offset inbetween VirtualAddress and VirtualAddress +
>>> Size.
>>>>>>>>>>>>>>>>> Using SafeintLib to do offset addition with result check.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>>>>>>>>>>>>>>>>> Cc: Jian J Wang <jian.j.wang@intel.com>
>>>>>>>>>>>>>>>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>>>>> Signed-off-by: Wenyi Xie <xiewenyi2@huawei.com>
>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>
>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>>>> ib.inf
>>>>>>>>>>>>> |
>>>>>>>>>>>>>>> 1 +
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>
>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>>>> ib.h
>>>>>>>>>>>>> |
>>>>>>>>>>>>>>> 1 +
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>
>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>> |
>>>>>>>>>>>>>>> 111 +++++++++++---------
>>>>>>>>>>>>>>>>>  3 files changed, 63 insertions(+), 50 deletions(-)
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>>>>
>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.inf
>>>>>>>>>>>>>>>
>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.inf
>>>>>>>>>>>>>>>>> index 1e1a639857e0..a7ac4830b3d4 100644
>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>
>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.inf
>>>>>>>>>>>>>>>>> +++
>>>>>>>>>>>>>>>
>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.inf
>>>>>>>>>>>>>>>>> @@ -53,6 +53,7 @@ [LibraryClasses]
>>>>>>>>>>>>>>>>>    SecurityManagementLib
>>>>>>>>>>>>>>>>>    PeCoffLib
>>>>>>>>>>>>>>>>>    TpmMeasurementLib
>>>>>>>>>>>>>>>>> +  SafeIntLib
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>  [Protocols]
>>>>>>>>>>>>>>>>>    gEfiFirmwareVolume2ProtocolGuid       ##
>>>>>>> SOMETIMES_CONSUMES
>>>>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>>>>
>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.h
>>>>>>>>>>>>>>>
>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.h
>>>>>>>>>>>>>>>>> index 17955ff9774c..060273917d5d 100644
>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>
>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.h
>>>>>>>>>>>>>>>>> +++
>>>>>>>>>>>>>>>
>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.h
>>>>>>>>>>>>>>>>> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-
>>>>> Patent
>>>>>>>>>>>>>>>>> #include <Library/DevicePathLib.h>  #include
>>>>>>>>>>>>>>>>> <Library/SecurityManagementLib.h>  #include
>>>>> <Library/PeCoffLib.h>
>>>>>>>>>>>>>>>>> +#include <Library/SafeIntLib.h>
>>>>>>>>>>>>>>>>>  #include <Protocol/FirmwareVolume2.h>  #include
>>>>>>>>>>>>>>>>> <Protocol/DevicePath.h>  #include <Protocol/BlockIo.h> diff -
>>> -
>>>>> git
>>>>>>>>>>>>>>>
>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>
>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>>> index 36b87e16d53d..dbc03e28c05b 100644
>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>
>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>>>>>>> .c
>>>>>>>>>>>>>>>>> +++
>>>>>>>>>>>>>>>
>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>>> @@ -1658,6 +1658,10 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>>    EFI_STATUS                           HashStatus;
>>>>>>>>>>>>>>>>>    EFI_STATUS                           DbStatus;
>>>>>>>>>>>>>>>>>    BOOLEAN                              IsFound;
>>>>>>>>>>>>>>>>> +  UINT32                               AlignedLength;
>>>>>>>>>>>>>>>>> +  UINT32                               Result;
>>>>>>>>>>>>>>>>> +  EFI_STATUS                           AddStatus;
>>>>>>>>>>>>>>>>> +  BOOLEAN                              IsAuthDataAssigned;
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>    SignatureList     = NULL;
>>>>>>>>>>>>>>>>>    SignatureListSize = 0;
>>>>>>>>>>>>>>>>> @@ -1667,6 +1671,7 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>>    Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
>>>>>>>>>>>>>>>>>    IsVerified        = FALSE;
>>>>>>>>>>>>>>>>>    IsFound           = FALSE;
>>>>>>>>>>>>>>>>> +  Result            = 0;
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>    //
>>>>>>>>>>>>>>>>>    // Check the image type and get policy setting.
>>>>>>>>>>>>>>>>> @@ -1850,9 +1855,10 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>>    // The first certificate starts at offset
>>>>>>>>>>>>>>>>> (SecDataDir->VirtualAddress) from
>>>>>>>>>>>>> the
>>>>>>>>>>>>>>> start of the file.
>>>>>>>>>>>>>>>>>    //
>>>>>>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>>>>>>>>> -       OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>> (WinCertificate-
>>>>>>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>>>>>>> +       (OffSet >= SecDataDir->VirtualAddress) && (OffSet <
>>>>>>>>>>>>>>>>> + (SecDataDir-
>>>>>>>>>>>>>>>> VirtualAddress + SecDataDir->Size));) {
>>>>>>>>>>>>>>>>> +    IsAuthDataAssigned = FALSE;
>>>>>>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>>> OffSet);
>>>>>>>>>>>>>>>>> +    AlignedLength = WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>>>>> (WinCertificate-
>>>>>>>>>>>>>>>> dwLength);
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> I disagree with this patch.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> The primary reason for my disagreement is that the bug report
>>>>>>>>>>>>>>>> <https://bugzilla.tianocore.org/show_bug.cgi?id=2215#c0> is
>>>>>>>>>>>>>>>> inexact, and so this patch tries to fix the wrong thing.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> With edk2 master at commit 65904cdbb33c, it is *not* possible
>>> to
>>>>>>>>>>>>>>>> overflow the OffSet variable to zero with "WinCertificate-
>>>>>>>> dwLength"
>>>>>>>>>>>>>>>> *purely*, and cause an endless loop. Note that we have (at
>>>>> commit
>>>>>>>>>>>>>>>> 65904cdbb33c):
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>   for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>>>>>>        OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>>>>>>>>        OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>>>>>>>> (WinCertificate-
>>>>>>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>>>>>>     WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>>> OffSet);
>>>>>>>>>>>>>>>>     if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>>>>>>>>> <= sizeof
>>>>>>>>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>>>>         (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <
>>>>>>>>>>>>> WinCertificate-
>>>>>>>>>>>>>>>> dwLength) {
>>>>>>>>>>>>>>>>       break;
>>>>>>>>>>>>>>>>     }
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> The last sub-condition checks whether the Security Data
>>> Directory
>>>>>>>>>>>>>>>> has enough room left for "WinCertificate->dwLength". If not,
>>> then
>>>>>>>>>>>>>>>> we break out of the loop.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> If we *do* have enough room, that is:
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>   (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) >=
>>>>>>>>>>>>> WinCertificate-
>>>>>>>>>>>>>>>> dwLength
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> then we have (by adding OffSet to both sides):
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size >= OffSet +
>>>>>>>>>>>>>>>> WinCertificate- dwLength
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> The left hand side is a known-good UINT32, and so
>>> incrementing
>>>>>>>>>>>>>>>> OffSet (a
>>>>>>>>>>>>>>>> UINT32) *solely* by "WinCertificate->dwLength" (also a
>>> UINT32)
>>>>>>>>>>>>>>>> does not cause an overflow.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Instead, the problem is with the alignment. The "if" statement
>>>>>>>>>>>>>>>> checks whether we have enough room for "dwLength", but
>>> then
>>>>>>>>>>>>>>>> "OffSet" is advanced by "dwLength" *aligned up* to the next
>>>>>>>>>>>>>>>> multiple of 8. And that may indeed cause various overflows.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Now, the main problem with the present patch is that it does
>>> not
>>>>>>>>>>>>>>>> fix one of those overflows. Namely, consider that "dwLength" is
>>>>>>>>>>>>>>>> very close to
>>>>>>>>>>>>>>>> MAX_UINT32 (or even think it's exactly MAX_UINT32). Then
>>>>> aligning
>>>>>>>>>>>>>>>> it up to the next multiple of 8 will yield 0. In other words,
>>>>>>>>> "AlignedLength"
>>>>>>>>>>>>>>>> will be zero.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> And when that happens, there's going to be an infinite loop just
>>>>>>>>>>>>>>>> the
>>>>>>>>>>>>>>>> same: "OffSet" will not be zero, but it will be *stuck*. The
>>>>>>>>>>>>>>>> SafeUint32Add() call at the bottom will succeed, but it will not
>>>>>>>>>>>>>>>> change the value of "OffSet".
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> More at the bottom.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>      if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>>>>>>>>>> <= sizeof
>>>>>>>>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>>>>>          (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>>>>>>>>>>>>>>>> <
>>>>>>>>>>>>>>> WinCertificate->dwLength) {
>>>>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>>>> @@ -1872,6 +1878,8 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>>>        AuthData   = PkcsCertData->CertData;
>>>>>>>>>>>>>>>>>        AuthDataSize = PkcsCertData->Hdr.dwLength -
>>>>>>>>>>>>>>>>> sizeof(PkcsCertData-
>>>>>>>>>>>>>> Hdr);
>>>>>>>>>>>>>>>>> +      IsAuthDataAssigned = TRUE;
>>>>>>>>>>>>>>>>> +      HashStatus = HashPeImageByType (AuthData,
>>> AuthDataSize);
>>>>>>>>>>>>>>>>>      } else if (WinCertificate->wCertificateType ==
>>>>>>>>>>>>> WIN_CERT_TYPE_EFI_GUID)
>>>>>>>>>>>>>>> {
>>>>>>>>>>>>>>>>>        //
>>>>>>>>>>>>>>>>>        // The certificate is formatted as
>>>>>>>>>>>>>>>>> WIN_CERTIFICATE_UEFI_GUID which
>>>>>>>>>>>>> is
>>>>>>>>>>>>>>> described in UEFI Spec.
>>>>>>>>>>>>>>>>> @@ -1880,72 +1888,75 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>>        if (WinCertUefiGuid->Hdr.dwLength <=
>>>>>>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
>>>>>>>>>>>>>>>>>          break;
>>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>>> -      if (!CompareGuid (&WinCertUefiGuid->CertType,
>>>>>>>>> &gEfiCertPkcs7Guid))
>>>>>>>>>>>>> {
>>>>>>>>>>>>>>>>> -        continue;
>>>>>>>>>>>>>>>>> +      if (CompareGuid (&WinCertUefiGuid->CertType,
>>>>>>>>>>>>>>>>> + &gEfiCertPkcs7Guid))
>>>>>>>>>>>>> {
>>>>>>>>>>>>>>>>> +        AuthData = WinCertUefiGuid->CertData;
>>>>>>>>>>>>>>>>> +        AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>>>>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>>>>>>>>>>>> +        IsAuthDataAssigned = TRUE;
>>>>>>>>>>>>>>>>> +        HashStatus = HashPeImageByType (AuthData,
>>>>> AuthDataSize);
>>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>>> -      AuthData = WinCertUefiGuid->CertData;
>>>>>>>>>>>>>>>>> -      AuthDataSize = WinCertUefiGuid->Hdr.dwLength -
>>>>>>>>>>>>>>> OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
>>>>>>>>>>>>>>>>>      } else {
>>>>>>>>>>>>>>>>>        if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE))
>>> {
>>>>>>>>>>>>>>>>>          break;
>>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>>> -      continue;
>>>>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> -    HashStatus = HashPeImageByType (AuthData,
>>> AuthDataSize);
>>>>>>>>>>>>>>>>> -    if (EFI_ERROR (HashStatus)) {
>>>>>>>>>>>>>>>>> -      continue;
>>>>>>>>>>>>>>>>> -    }
>>>>>>>>>>>>>>>>> -
>>>>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>>>>> -    // Check the digital signature against the revoked
>>> certificate
>>>>> in
>>>>>>>>>>>>> forbidden
>>>>>>>>>>>>>>> database (dbx).
>>>>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>>>>> -    if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>>>>>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>>>>>>>>>>>>> -      IsVerified = FALSE;
>>>>>>>>>>>>>>>>> -      break;
>>>>>>>>>>>>>>>>> -    }
>>>>>>>>>>>>>>>>> -
>>>>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>>>>> -    // Check the digital signature against the valid certificate in
>>>>>>>>> allowed
>>>>>>>>>>>>>>> database (db).
>>>>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>>>>> -    if (!IsVerified) {
>>>>>>>>>>>>>>>>> -      if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>>>>>>>>>>>>> -        IsVerified = TRUE;
>>>>>>>>>>>>>>>>> +    if (IsAuthDataAssigned && !EFI_ERROR (HashStatus)) {
>>>>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>>>>> +      // Check the digital signature against the revoked
>>>>>>>>>>>>>>>>> + certificate in
>>>>>>>>>>>>> forbidden
>>>>>>>>>>>>>>> database (dbx).
>>>>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>>>>> +      if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
>>>>>>>>>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
>>>>>>>>>>>>>>>>> +        IsVerified = FALSE;
>>>>>>>>>>>>>>>>> +        break;
>>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>>> -    }
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>>>>> -    // Check the image's hash value.
>>>>>>>>>>>>>>>>> -    //
>>>>>>>>>>>>>>>>> -    DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>>>>>>>>> -                 EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>>>>>>>>>>>> -                 mImageDigest,
>>>>>>>>>>>>>>>>> -                 &mCertType,
>>>>>>>>>>>>>>>>> -                 mImageDigestSize,
>>>>>>>>>>>>>>>>> -                 &IsFound
>>>>>>>>>>>>>>>>> -                 );
>>>>>>>>>>>>>>>>> -    if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>>>>>>>>>>>>> -      Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>>>>>>>>>>>>> -      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image
>>> is
>>>>>>>>> signed
>>>>>>>>>>>>> but %s
>>>>>>>>>>>>>>> hash of image is found in DBX.\n", mHashTypeStr));
>>>>>>>>>>>>>>>>> -      IsVerified = FALSE;
>>>>>>>>>>>>>>>>> -      break;
>>>>>>>>>>>>>>>>> -    }
>>>>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>>>>> +      // Check the digital signature against the valid
>>>>>>>>>>>>>>>>> + certificate in allowed
>>>>>>>>>>>>>>> database (db).
>>>>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>>>>> +      if (!IsVerified) {
>>>>>>>>>>>>>>>>> +        if (IsAllowedByDb (AuthData, AuthDataSize)) {
>>>>>>>>>>>>>>>>> +          IsVerified = TRUE;
>>>>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>>>>> +      }
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> -    if (!IsVerified) {
>>>>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>>>>> +      // Check the image's hash value.
>>>>>>>>>>>>>>>>> +      //
>>>>>>>>>>>>>>>>>        DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>>>>>>>>> -                   EFI_IMAGE_SECURITY_DATABASE,
>>>>>>>>>>>>>>>>> +                   EFI_IMAGE_SECURITY_DATABASE1,
>>>>>>>>>>>>>>>>>                     mImageDigest,
>>>>>>>>>>>>>>>>>                     &mCertType,
>>>>>>>>>>>>>>>>>                     mImageDigestSize,
>>>>>>>>>>>>>>>>>                     &IsFound
>>>>>>>>>>>>>>>>>                     );
>>>>>>>>>>>>>>>>> -      if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>>>>>>>>>>>>> -        IsVerified = TRUE;
>>>>>>>>>>>>>>>>> -      } else {
>>>>>>>>>>>>>>>>> -        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image
>>> is
>>>>>>>>> signed
>>>>>>>>>>>>> but
>>>>>>>>>>>>>>> signature is not allowed by DB and %s hash of image is not found
>>> in
>>>>>>>>>>>>> DB/DBX.\n",
>>>>>>>>>>>>>>> mHashTypeStr));
>>>>>>>>>>>>>>>>> +      if (EFI_ERROR (DbStatus) || IsFound) {
>>>>>>>>>>>>>>>>> +        Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
>>>>>>>>>>>>>>>>> +        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image
>>> is
>>>>>>>>>>>>>>>>> + signed
>>>>>>>>>>>>>>> but %s hash of image is found in DBX.\n", mHashTypeStr));
>>>>>>>>>>>>>>>>> +        IsVerified = FALSE;
>>>>>>>>>>>>>>>>> +        break;
>>>>>>>>>>>>>>>>>        }
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +      if (!IsVerified) {
>>>>>>>>>>>>>>>>> +        DbStatus = IsSignatureFoundInDatabase (
>>>>>>>>>>>>>>>>> +                     EFI_IMAGE_SECURITY_DATABASE,
>>>>>>>>>>>>>>>>> +                     mImageDigest,
>>>>>>>>>>>>>>>>> +                     &mCertType,
>>>>>>>>>>>>>>>>> +                     mImageDigestSize,
>>>>>>>>>>>>>>>>> +                     &IsFound
>>>>>>>>>>>>>>>>> +                     );
>>>>>>>>>>>>>>>>> +        if (!EFI_ERROR (DbStatus) && IsFound) {
>>>>>>>>>>>>>>>>> +          IsVerified = TRUE;
>>>>>>>>>>>>>>>>> +        } else {
>>>>>>>>>>>>>>>>> +          DEBUG ((DEBUG_INFO, "DxeImageVerificationLib:
>>> Image
>>>>> is
>>>>>>>>>>>>>>>>> + signed
>>>>>>>>>>>>> but
>>>>>>>>>>>>>>> signature is not allowed by DB and %s hash of image is not found
>>> in
>>>>>>>>>>>>> DB/DBX.\n",
>>>>>>>>>>>>>>> mHashTypeStr));
>>>>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>>>>> +      }
>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    AddStatus = SafeUint32Add (OffSet, AlignedLength,
>>> &Result);
>>>>>>>>>>>>>>>>> +    if (EFI_ERROR (AddStatus)) {
>>>>>>>>>>>>>>>>> +      break;
>>>>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>>>> +    OffSet = Result;
>>>>>>>>>>>>>>>>>    }
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>    if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
>>>>>>>>>>>>>>>>> {
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> There are other (smaller) reasons why I dislike this patch:
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> - The "IsAuthDataAssigned" variable is superfluous; we could
>>> use
>>>>>>>>>>>>>>>> the existent "AuthData" variable (with a NULL-check and a
>>>>>>>>>>>>>>>> NULL-assignment) similarly.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> - The patch complicates / reorganizes the control flow
>>> needlessly.
>>>>>>>>>>>>>>>> This complication originates from placing the checked "OffSet"
>>>>>>>>>>>>>>>> increment at the bottom of the loop, which then requires the
>>>>>>>>>>>>>>>> removal of all the "continue" statements. But we don't need to
>>>>>>>>>>>>>>>> check-and-increment at the bottom. We can keep the
>>> increment
>>>>>>>>>>>>>>>> inside the "for" statement, only extend the *existent* room
>>> check
>>>>>>>>>>>>>>>> (which I've quoted) to take the alignment into account as well.
>>> If
>>>>>>>>>>>>>>>> there is enough room for the alignment in the security data
>>>>>>>>>>>>>>>> directory, then that guarantees there won't be a UINT32
>>> overflow
>>>>>>>>> either.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> All in all, I'm proposing the following three patches instead. The
>>>>>>>>>>>>>>>> first two patches are preparation, the last patch is the fix.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Patch#1:
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> From 11af0a104d34d39bf1b1aab256428ae4edbddd77 Mon
>>> Sep
>>>>> 17
>>>>>>>>>>>>> 00:00:00
>>>>>>>>>>>>>>> 2001
>>>>>>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:11:39 +0200
>>>>>>>>>>>>>>>>> Subject: [PATCH 1/3] SecurityPkg/DxeImageVerificationLib:
>>>>> extract
>>>>>>>>>>>>>>>>> SecDataDirEnd, SecDataDirLeft
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> The following two quantities:
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size - OffSet
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> are used multiple times in DxeImageVerificationHandler().
>>>>>>>>>>>>>>>>> Introduce helper variables for them: "SecDataDirEnd" and
>>>>>>>>> "SecDataDirLeft", respectively.
>>>>>>>>>>>>>>>>> This saves us multiple calculations and significantly simplifies
>>> the
>>>>>>> code.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Note that all three summands above have type UINT32,
>>>>> therefore
>>>>>>>>>>>>>>>>> the new variables are also of type UINT32.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> This patch does not change behavior.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> (Note that the code already handles the case when the
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>   SecDataDir->VirtualAddress + SecDataDir->Size
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> UINT32 addition overflows -- namely, in that case, the
>>>>>>>>>>>>>>>>> certificate loop is never entered, and the corruption check
>>> right
>>>>>>>>>>>>>>>>> after the loop fires.)
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>
>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>>>> ib.c |
>>>>>>>>>>>>> 12
>>>>>>>>>>>>>>> ++++++++----
>>>>>>>>>>>>>>>>>  1 file changed, 8 insertions(+), 4 deletions(-)
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>>>>
>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>
>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>>> index 36b87e16d53d..8761980c88aa 100644
>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>
>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>>>>>>> .c
>>>>>>>>>>>>>>>>> +++
>>>>>>>>>>>>>>>
>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>>> @@ -1652,6 +1652,8 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>>    UINT8                                *AuthData;
>>>>>>>>>>>>>>>>>    UINTN                                AuthDataSize;
>>>>>>>>>>>>>>>>>    EFI_IMAGE_DATA_DIRECTORY             *SecDataDir;
>>>>>>>>>>>>>>>>> +  UINT32                               SecDataDirEnd;
>>>>>>>>>>>>>>>>> +  UINT32                               SecDataDirLeft;
>>>>>>>>>>>>>>>>>    UINT32                               OffSet;
>>>>>>>>>>>>>>>>>    CHAR16                               *NameStr;
>>>>>>>>>>>>>>>>>    RETURN_STATUS                        PeCoffStatus;
>>>>>>>>>>>>>>>>> @@ -1849,12 +1851,14 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>>    // "Attribute Certificate Table".
>>>>>>>>>>>>>>>>>    // The first certificate starts at offset
>>>>>>>>>>>>>>>>> (SecDataDir->VirtualAddress) from
>>>>>>>>>>>>> the
>>>>>>>>>>>>>>> start of the file.
>>>>>>>>>>>>>>>>>    //
>>>>>>>>>>>>>>>>> +  SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir-
>>>>>> Size;
>>>>>>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>>>>>>> -       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
>>>>>>>>>>>>>>>>> +       OffSet < SecDataDirEnd;
>>>>>>>>>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>>>>>>>>> (WinCertificate-
>>>>>>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>>> OffSet);
>>>>>>>>>>>>>>>>> -    if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>> <=
>>>>>>>>> sizeof
>>>>>>>>>>>>>>> (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>>>>> -        (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet)
>>> <
>>>>>>>>>>>>>>> WinCertificate->dwLength) {
>>>>>>>>>>>>>>>>> +    SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>>>>>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>>>>> +        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> @@ -1948,7 +1952,7 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>>>>    }
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> -  if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size))
>>>>>>>>>>>>>>>>> {
>>>>>>>>>>>>>>>>> +  if (OffSet != SecDataDirEnd) {
>>>>>>>>>>>>>>>>>      //
>>>>>>>>>>>>>>>>>      // The Size in Certificate Table or the attribute
>>>>>>>>>>>>>>>>> certificate table is
>>>>>>>>>>>>> corrupted.
>>>>>>>>>>>>>>>>>      //
>>>>>>>>>>>>>>>>> --
>>>>>>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Patch#2:
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> From 72012c065a53582f7df695e7b9730c45f49226c6 Mon Sep
>>>>> 17
>>>>>>>>> 00:00:00
>>>>>>>>>>>>>>> 2001
>>>>>>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:19:06 +0200
>>>>>>>>>>>>>>>>> Subject: [PATCH 2/3] SecurityPkg/DxeImageVerificationLib:
>>>>> assign
>>>>>>>>>>>>>>>>> WinCertificate after size check
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Currently the (SecDataDirLeft <= sizeof (WIN_CERTIFICATE))
>>>>> check
>>>>>>>>>>>>>>>>> only guards the de-referencing of the "WinCertificate" pointer.
>>>>>>>>>>>>>>>>> It does not guard the calculation of hte pointer itself:
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>   WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>>> OffSet);
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> This is wrong; if we don't know for sure that we have enough
>>>>> room
>>>>>>>>>>>>>>>>> for a WIN_CERTIFICATE, then even creating such a pointer,
>>> not
>>>>>>>>>>>>>>>>> just de-referencing it, may invoke undefined behavior.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Move the pointer calculation after the size check.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>
>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>>>> ib.c |
>>>>>>>>>>>>> 8
>>>>>>>>>>>>>>> +++++---
>>>>>>>>>>>>>>>>>  1 file changed, 5 insertions(+), 3 deletions(-)
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>>>>
>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>
>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>>> index 8761980c88aa..461ed7cfb5ac 100644
>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>
>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>>>>>>> .c
>>>>>>>>>>>>>>>>> +++
>>>>>>>>>>>>>>>
>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>>> @@ -1855,10 +1855,12 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>>    for (OffSet = SecDataDir->VirtualAddress;
>>>>>>>>>>>>>>>>>         OffSet < SecDataDirEnd;
>>>>>>>>>>>>>>>>>         OffSet += (WinCertificate->dwLength + ALIGN_SIZE
>>>>>>>>>>>>>>>>> (WinCertificate-
>>>>>>>>>>>>>>>> dwLength))) {
>>>>>>>>>>>>>>>>> -    WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>>>>> OffSet);
>>>>>>>>>>>>>>>>>      SecDataDirLeft = SecDataDirEnd - OffSet;
>>>>>>>>>>>>>>>>> -    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE) ||
>>>>>>>>>>>>>>>>> -        SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>>>>>> +    if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) {
>>>>>>>>>>>>>>>>> +      break;
>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>> +    WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>>>>> OffSet);
>>>>>>>>>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> --
>>>>>>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Patch#3:
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> From 0bbba15b84f8f9f2cdc770a89f418aaec6cfb31e Mon Sep
>>> 17
>>>>>>>>> 00:00:00
>>>>>>>>>>>>>>> 2001
>>>>>>>>>>>>>>>>> From: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>>>>> Date: Thu, 13 Aug 2020 19:34:33 +0200
>>>>>>>>>>>>>>>>> Subject: [PATCH 3/3] SecurityPkg/DxeImageVerificationLib:
>>> catch
>>>>>>>>>>>>> alignment
>>>>>>>>>>>>>>>>>  overflow (CVE-2019-14562)
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> The DxeImageVerificationHandler() function currently checks
>>>>>>>>>>>>>>>>> whether "SecDataDir" has enough room for
>>>>>>>>>>>>>>>>> "WinCertificate->dwLength". However,
>>>>>>>>>>>>>>> for
>>>>>>>>>>>>>>>>> advancing "OffSet", "WinCertificate->dwLength" is aligned to
>>> the
>>>>>>>>>>>>>>>>> next multiple of 8. If "WinCertificate->dwLength" is large
>>>>>>>>>>>>>>>>> enough, the alignment will return 0, and "OffSet" will be stuck
>>> at
>>>>>>> the
>>>>>>>>> same value.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Check whether "SecDataDir" has room left for both
>>>>>>>>>>>>>>>>> "WinCertificate->dwLength" and the alignment.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>
>>>>>>> SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>>>> ib.c |
>>>>>>>>>>>>> 4
>>>>>>>>>>>>>>> +++-
>>>>>>>>>>>>>>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> diff --git
>>>>>>>>>>>>>>>
>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>
>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>>> index 461ed7cfb5ac..e38eb981b7a0 100644
>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>
>>>>>>> a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib
>>>>>>>>>>>>> .c
>>>>>>>>>>>>>>>>> +++
>>>>>>>>>>>>>>>
>>>>>>> b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationL
>>>>>>>>>>>>>>> ib.c
>>>>>>>>>>>>>>>>> @@ -1860,7 +1860,9 @@ DxeImageVerificationHandler (
>>>>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>>>>      WinCertificate = (WIN_CERTIFICATE *) (mImageBase +
>>> OffSet);
>>>>>>>>>>>>>>>>> -    if (SecDataDirLeft < WinCertificate->dwLength) {
>>>>>>>>>>>>>>>>> +    if (SecDataDirLeft < WinCertificate->dwLength ||
>>>>>>>>>>>>>>>>> +        (SecDataDirLeft - WinCertificate->dwLength <
>>>>>>>>>>>>>>>>> +         ALIGN_SIZE (WinCertificate->dwLength))) {
>>>>>>>>>>>>>>>>>        break;
>>>>>>>>>>>>>>>>>      }
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> --
>>>>>>>>>>>>>>>>> 2.19.1.3.g30247aa5d201
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> If Wenyi and the reviewers are OK with these patches, I can
>>>>> submit
>>>>>>>>>>>>>>>> them as a standalone patch series.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Note that I do not have any reproducer for the issue; the best
>>>>>>>>>>>>>>>> testing that I could offer would be some light-weight Secure
>>> Boot
>>>>>>>>>>>>>>>> regression tests.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Thanks
>>>>>>>>>>>>>>>> Laszlo
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> .
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> .
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>
>>>
>>> 
>>
> 


^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2020-09-01  7:56 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-08-13 11:55 [PATCH EDK2 v2 0/1] SecurityPkg/DxeImageVerificationLib:Enhanced verification of Offset wenyi,xie
2020-08-13 11:55 ` [PATCH EDK2 v2 1/1] " wenyi,xie
2020-08-13 14:32   ` Yao, Jiewen
2020-08-13 18:50   ` Laszlo Ersek
2020-08-14  7:53     ` wenyi,xie
2020-08-14  8:53       ` [edk2-devel] " Yao, Jiewen
2020-08-14 11:29         ` wenyi,xie
2020-08-17 16:52         ` Laszlo Ersek
2020-08-17 23:23           ` Yao, Jiewen
2020-08-18 10:17             ` Laszlo Ersek
     [not found]               ` <a7elBrHZ3zD0Stt3MiPOUU_6uOnp-LlR4c9weDhWm4xYH388XWK0M80fLZe_AqbzF68IFK_IdkWQtKN8HKyRnQ==@protonmail.internalid>
2020-08-18 10:24               ` Marvin Häuser
2020-08-18 11:01                 ` Laszlo Ersek
2020-08-18 12:21                 ` Vitaly Cheptsov
2020-08-18 13:12               ` Yao, Jiewen
2020-08-18 17:29                 ` Bret Barkelew
2020-08-18 23:00                   ` Yao, Jiewen
2020-08-19  9:33                   ` Laszlo Ersek
2020-08-18  2:10           ` Wang, Jian J
2020-08-18  8:58             ` Laszlo Ersek
2020-08-18 15:18               ` john.mathews
2020-08-19  9:26                 ` Laszlo Ersek
2020-08-28  3:17                   ` wenyi,xie
2020-08-28  3:50                     ` Yao, Jiewen
2020-08-28  6:18                       ` wenyi,xie
2020-08-28  6:43                         ` Yao, Jiewen
2020-08-31 11:23                           ` wenyi,xie
2020-08-31 16:06                             ` Yao, Jiewen
2020-09-01  7:10                               ` wenyi,xie
2020-09-01  7:31                                 ` Yao, Jiewen
2020-09-01  7:43                                   ` wenyi,xie
2020-09-01  7:56                                     ` Laszlo Ersek
2020-09-01  7:29                               ` Laszlo Ersek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox