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>,
	Sean Brogan <sean.brogan@microsoft.com>
Subject: Re: [Patch] MdePkg/BaseSafeIntLib: Add SafeIntLib class and instance
Date: Thu, 8 Feb 2018 01:32:14 +0100	[thread overview]
Message-ID: <656eb64b-3265-f021-ff4f-df2ed6b7c752@redhat.com> (raw)
In-Reply-To: <20171219193625.16060-1-michael.d.kinney@intel.com>

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)))

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


  parent reply	other threads:[~2018-02-08  0:26 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 [this message]
2018-02-08  0:45   ` Laszlo Ersek
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=656eb64b-3265-f021-ff4f-df2ed6b7c752@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