public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
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
> 



  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