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 <ler...@redhat.com> 
> Sent: Tuesday, August 18, 2020 1:59 AM
> To: Wang, Jian J <jian.j.w...@intel.com>; devel@edk2.groups.io; Yao, Jiewen 
> <jiewen....@intel.com>; xiewen...@huawei.com
> Cc: huangmin...@huawei.com; songdongku...@huawei.com; Mathews, John 
> <john.math...@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....@intel.com>; devel@edk2.groups.io; 
>>> xiewen...@huawei.com; Wang, Jian J <jian.j.w...@intel.com>
>>> Cc: huangmin...@huawei.com; songdongku...@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 <ler...@redhat.com>; devel@edk2.groups.io; Yao, 
>>>>> Jiewen <jiewen....@intel.com>; Wang, Jian J <jian.j.w...@intel.com>
>>>>> Cc: huangmin...@huawei.com; songdongku...@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....@intel.com>
>>>>>>> Cc: Jian J Wang <jian.j.w...@intel.com>
>>>>>>> Cc: Laszlo Ersek <ler...@redhat.com>
>>>>>>> Signed-off-by: Wenyi Xie <xiewen...@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 <ler...@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 <ler...@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 <ler...@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 <ler...@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 <ler...@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 <ler...@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
>>>>>>
>>>>>>
>>>>>> .
>>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>
>>>
>>> 
>>
> 


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.

View/Reply Online (#64421): https://edk2.groups.io/g/devel/message/64421
Mute This Topic: https://groups.io/mt/76165658/21656
Group Owner: devel+ow...@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub  [arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to