From: Laszlo Ersek <lersek@redhat.com>
To: "Kinney, Michael D" <michael.d.kinney@intel.com>,
edk2-devel@lists.01.org
Cc: Liming Gao <liming.gao@intel.com>
Subject: Re: [Patch] MdePkg/BaseSafeIntLib: Add SafeIntLib class and instance
Date: Thu, 8 Feb 2018 01:45:13 +0100 [thread overview]
Message-ID: <d4cc50f8-9d67-9eb0-b5bc-857b78d48229@redhat.com> (raw)
In-Reply-To: <656eb64b-3265-f021-ff4f-df2ed6b7c752@redhat.com>
On 02/08/18 01:32, Laszlo Ersek wrote:
> On 12/19/17 20:36, Kinney, Michael D wrote:
>> From: Sean Brogan <sean.brogan@microsoft.com>
>>
>> SafeIntLib provides helper functions to prevent integer overflow
>> during type conversion, addition, subtraction, and multiplication.
>
> I clearly cannot review such a huge patch, but I've noticed something
> and would like to ask for clarification:
>
>> +/**
>> + INT64 Subtraction
>> +
>> + Performs the requested operation using the input parameters into a value
>> + specified by Result type and stores the converted value into the caller
>> + allocated output buffer specified by Result. The caller must pass in a
>> + Result buffer that is at least as large as the Result type.
>> +
>> + If Result is NULL, RETURN_INVALID_PARAMETER is returned.
>> +
>> + If the requested operation results in an overflow or an underflow condition,
>> + then Result is set to INT64_ERROR and RETURN_BUFFER_TOO_SMALL is returned.
>> +
>> + @param[in] Minuend A number from which another is to be subtracted.
>> + @param[in] Subtrahend A number to be subtracted from another
>> + @param[out] Result Pointer to the result of subtraction
>> +
>> + @retval RETURN_SUCCESS Successful subtraction
>> + @retval RETURN_BUFFER_TOO_SMALL Underflow
>> + @retval RETURN_INVALID_PARAMETER Result is NULL
>> +**/
>> +RETURN_STATUS
>> +EFIAPI
>> +SafeInt64Sub (
>> + IN INT64 Minuend,
>> + IN INT64 Subtrahend,
>> + OUT INT64 *Result
>> + )
>> +{
>> + RETURN_STATUS Status;
>> + INT64 SignedResult;
>> +
>> + if (Result == NULL) {
>> + return RETURN_INVALID_PARAMETER;
>> + }
>> +
>> + SignedResult = Minuend - Subtrahend;
>> +
>> + //
>> + // Subtracting a positive number from a positive number never overflows.
>> + // Subtracting a negative number from a negative number never overflows.
>> + // If you subtract a negative number from a positive number, you expect a positive result.
>> + // If you subtract a positive number from a negative number, you expect a negative result.
>> + // Overflow if inputs vary in sign and the output does not have the same sign as the first input.
>> + //
>> + if (((Minuend < 0) != (Subtrahend < 0)) &&
>> + ((Minuend < 0) != (SignedResult < 0))) {
>> + *Result = INT64_ERROR;
>> + Status = RETURN_BUFFER_TOO_SMALL;
>> + } else {
>> + *Result = SignedResult;
>> + Status = RETURN_SUCCESS;
>> + }
>> +
>> + return Status;
>> +}
>
> Is our goal to
>
> (a) catch overflows before the caller goes wrong due to them, or
> (b) completely prevent undefined behavior from happening, even inside
> SafeInt64Sub()?
>
> The above implementation may be good for (a), but it's not correct for
> (b). The
>
> SignedResult = Minuend - Subtrahend;
>
> subtraction invokes undefined behavior if the result cannot be
> represented [*]; the rest of the code cannot help.
>
> Now if we say that such subtractions always occur according to the
> "usual two's complement definition", on all architectures that edk2
> targets, and we're sure that no compiler or analysis tool will flag --
> or exploit -- the UB either, then the code is fine; meaning our choice
> is (a).
>
> But, if (b) is our goal, I would replace the current error condition with:
>
> (((Subtrahend > 0) && (Minuend < MIN_INT64 + Subtrahend)) ||
> ((Subtrahend < 0) && (Minuend > MAX_INT64 + Subtrahend)))
To clarify, I wouldn't just replace the error condition. In addition to
that, I would remove the SignedResult helper variable (together with the
current subtraction), and calculate & assign
*Result = Minuend - Subtrahend;
only after the error condition fails (i.e. the subtraction is safe).
Thanks,
Laszlo
> Justification:
>
> * Subtrahend==0 can never cause overflow
>
> * Subtrahend>0 can only cause overflow at the negative end, so check
> that: (Minuend - Subtrahend < MIN_INT64), mathematically speaking.
> In order to write that down in C, add Subtrahend (a positive value)
> to both sides, yielding (Minuend < MIN_INT64 + Subtrahend). Here,
> (MIN_INT64 + Subtrahend) will never go out of range, because
> Subtrahend is positive, and (MIN_INT64 + MAX_INT64) is representable.
>
> * Subtrahend<0 can only cause overflow at the positive end, so check
> that: (Minuend - Subtrahend > MAX_INT64), mathematically speaking.
> In order to write that down in C, add Subtrahend (a negative value)
> to both sides, yielding (Minuend > MAX_INT64 + Subtrahend). Here,
> (MAX_INT64 + Subtrahend) will never go out of range, because
> Subtrahend is negative, and (MAX_INT64 + MIN_INT64) is representable.
>
> (
>
> [*] ISO C99 section 6.5 Expressions, p5: "If an exceptional condition
> occurs during the evaluation of an expression (that is, if the result is
> not mathematically defined or not in the range of representable values
> for its type), the behavior is undefined."
>
> Section 6.2.5 Types, p9 only exempts unsigned integers, "A computation
> involving unsigned operands can never overflow, because a result that
> cannot be represented by the resulting unsigned integer type is reduced
> modulo the number that is one greater than the largest value that can be
> represented by the resulting type."
>
> Note that this is different from conversion, where the computation first
> succeeds (= we have a value), and then the value is converted to a type
> that causes truncation: section 6.3.1.3 Signed and unsigned integers,
> p3: "Otherwise, the new type is signed and the value cannot be
> represented in it; either the result is implementation-defined or an
> implementation-defined signal is raised."
>
> In the code above, the expression (Minuend - Subtrahend) can invoke
> undefined behavior, there is no conversion (not even as part of the
> assignment to SignedResult).
>
> )
>
> Thanks,
> Laszlo
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel
>
next prev parent reply other threads:[~2018-02-08 0:39 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-12-19 19:36 [Patch] MdePkg/BaseSafeIntLib: Add SafeIntLib class and instance Kinney, Michael D
2018-01-18 8:09 ` Sean Brogan
2018-01-19 7:48 ` Gao, Liming
2018-02-08 0:32 ` Laszlo Ersek
2018-02-08 0:45 ` Laszlo Ersek [this message]
2018-02-13 12:23 ` Laszlo Ersek
2018-02-13 16:17 ` Kinney, Michael D
2018-02-13 16:56 ` Bret Barkelew
2018-02-13 17:15 ` Andrew Fish
2018-02-13 17:55 ` Laszlo Ersek
2018-02-13 17:29 ` Laszlo Ersek
2018-02-13 18:19 ` Bret Barkelew
2018-02-13 17:18 ` Ard Biesheuvel
2018-02-13 17:51 ` Laszlo Ersek
2018-02-13 17:55 ` Ard Biesheuvel
2018-02-13 18:03 ` 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=d4cc50f8-9d67-9eb0-b5bc-857b78d48229@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