From: "Laszlo Ersek" <lersek@redhat.com>
To: "Philippe Mathieu-Daudé" <philmd@redhat.com>, devel@edk2.groups.io
Cc: Jian J Wang <jian.j.wang@intel.com>,
Jiewen Yao <jiewen.yao@intel.com>, Min Xu <min.m.xu@intel.com>,
Wenyi Xie <xiewenyi2@huawei.com>
Subject: Re: [edk2-devel] [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch alignment overflow (CVE-2019-14562)
Date: Tue, 1 Sep 2020 18:52:10 +0200 [thread overview]
Message-ID: <f3b5cf62-706d-19d6-d786-c1567ad06ef4@redhat.com> (raw)
In-Reply-To: <7fae1361-e773-bb0f-21c8-fd548b4bbdab@redhat.com>
On 09/01/20 17:53, Philippe Mathieu-Daudé wrote:
> Hi Laszlo,
>
> On 9/1/20 11:12 AM, Laszlo Ersek wrote:
>> 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.
>>
>> Cc: Jian J Wang <jian.j.wang@intel.com>
>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>> Cc: Min Xu <min.m.xu@intel.com>
>> Cc: Wenyi Xie <xiewenyi2@huawei.com>
>> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2215
>> 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 100739eb3eb6..11154b6cc58a 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))) {
>
> I dare to ask (probably again, I remember some similar boundary check
> style question once), why not as (which is simpler for me to review):
>
> if (SecDataDirLeft < WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength)) {
Indeed, the mathematical relation that we want to catch (and reject) is:
SecDataDirLeft < WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength)
But the problem is precisely with the addition on the right hand side.
(1) The "WinCertificate->dwLength" field is of type UINT32 (meaning, in
standard C terms, "unsigned int").
(2) The ALIGN_SIZE() macro is defined as follows, in
"SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h":
#define ALIGNMENT_SIZE 8
#define ALIGN_SIZE(a) (((a) % ALIGNMENT_SIZE) ? ALIGNMENT_SIZE - ((a) % ALIGNMENT_SIZE) : 0)
If you substitute "WinCertificate->dwLength" for "a", and 8 for
ALIGNMENT_SIZE, you get the following replacement text for the macro
invocation:
(((WinCertificate->dwLength) % 8) ? 8 - ((WinCertificate->dwLength) % 8) : 0)
Here the third operand of the conditional operator (that is, 0) is
of type "int".
The 2nd operand of the conditional operator ultimately has type
"unsigned int". That's because the type of
"WinCertificate->dwLength" conceptually "propagates" outwards across
the "%" and "-" operators, via the "usual arithmetic conversions".
Basically you have
(unsigned int) % (int) -->
converts the int to unsigned int, produces unsigned int
(int) - (unsigned int) -->
converts the int to unsigned int, produces unsigned int
And then we have the following, for the conditional operator too
(C99 6.5.15p5):
If both the second and third operands have arithmetic type, the
result type that would be determined by the usual arithmetic
conversions, were they applied to those two operands, is the type
of the result.
Which means that ALIGN_SIZE (WinCertificate->dwLength) produces a
UINT32 (unsigned int) as well.
(3) Furthermore, "SecDataDirLeft" is of type UINT32 (unsigned int) too.
(4) Ultimately, in the 2nd subcondition of the "if", we would have
UINT32 < UINT32 + UINT32
If the addition overflows on the right hand size (which is well
defined behavior for "unsigned int"s), possibly to a really small
number, then the comparison may evaluate to FALSE, even though the
*mathematical* result would be TRUE.
Specifically, if
WinCertificate->dwLength >= MAX_UINT32 - 6
then the sum
WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength)
wraps around to zero precisely, and then the 2nd subcondition would
decay to
SecDataDirLeft < 0
which would always evaluate to FALSE.
We can avoid this by taking the mathematically relevant expression
SecDataDirLeft < WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength)
and by subtracting "WinCertificate->dwLength" from both sides. That
yields the following (mathematically equivalent) inequality:
SecDataDirLeft - WinCertificate->dwLength < ALIGN_SIZE (WinCertificate->dwLength)
which we can safely evaluate in C.
The reason for that "safety" is the *directly preceding* subcondition in
the "if" statement:
if (SecDataDirLeft < WinCertificate->dwLength || ...
Namely, if this (1st) condition evaluates to TRUE, then we ignore the second
subcondition (due to the logical OR), and take the "break" at once. And
if the 1st condition evaluates to FALSE (so that we need to consider the
new, 2nd, subcondition), *then* we know:
SecDataDirLeft >= WinCertificate->dwLength
and then just subtract "WinCertificate->dwLength" from both sides, to
realize the following mathematical assurance:
SecDataDirLeft - WinCertificate->dwLength >= 0
Thus, the difference that is visible on the left-hand side above is
safe to *express* in the second subcondition that we actually care
about:
SecDataDirLeft - WinCertificate->dwLength < ALIGN_SIZE (WinCertificate->dwLength)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
this UINT32 subtraction will not
underflow because the first subcondition
evaluated to FALSE
In brief,
- we re-formulate the 2nd subcondition, using a subtraction in place of
the addition, for preventing an overflow in the addition,
- and the subtraction is safe due to the 1st subcondition, which we
check directly before.
So it's not just a style question, but a safety/security one.
> At any rate, for this patch:
> Reviewed-by: Philippe Mathieu-Daude <philmd@redhat.com>
Thanks!
Laszlo
>
>> break;
>> }
>>
>>
>
next prev parent reply other threads:[~2020-09-01 16:52 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-09-01 9:12 [PATCH 0/3] SecurityPkg/DxeImageVerificationLib: catch alignment overflow (CVE-2019-14562) Laszlo Ersek
2020-09-01 9:12 ` [PATCH 1/3] SecurityPkg/DxeImageVerificationLib: extract SecDataDirEnd, SecDataDirLeft Laszlo Ersek
2020-09-01 15:37 ` [edk2-devel] " Philippe Mathieu-Daudé
2020-09-02 1:31 ` wenyi,xie
2020-09-02 1:55 ` Xu, Min M
2020-09-01 9:12 ` [PATCH 2/3] SecurityPkg/DxeImageVerificationLib: assign WinCertificate after size check Laszlo Ersek
2020-09-01 15:40 ` [edk2-devel] " Philippe Mathieu-Daudé
2020-09-02 1:32 ` wenyi,xie
2020-09-02 2:19 ` Xu, Min M
2020-09-01 9:12 ` [PATCH 3/3] SecurityPkg/DxeImageVerificationLib: catch alignment overflow (CVE-2019-14562) Laszlo Ersek
2020-09-01 15:53 ` [edk2-devel] " Philippe Mathieu-Daudé
2020-09-01 16:52 ` Laszlo Ersek [this message]
2020-09-02 1:32 ` wenyi,xie
2020-09-02 2:34 ` Xu, Min M
2020-09-02 4:02 ` [edk2-devel] [PATCH 0/3] " Yao, Jiewen
2020-09-02 6:35 ` Laszlo Ersek
2020-09-02 6:41 ` Yao, Jiewen
2020-09-02 6:46 ` Laszlo Ersek
2020-09-02 8:58 ` 回复: " gaoliming
2020-09-02 10:33 ` Laszlo Ersek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=f3b5cf62-706d-19d6-d786-c1567ad06ef4@redhat.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox