Re: Observability of NaN distinctions — is this a concern?

Allen Wirfs-Brock allen at wirfs-brock.com
Tue Mar 26 11:55:51 PDT 2013


On Mar 26, 2013, at 1:29 AM, Oliver Hunt wrote:

> 
> Ok, I'll try to go over this again, because for whatever reason it doesn't appear to stick:
> 
> If you have a double-typed array, and access a member:
> typedArray[0]
> 
> Then in ES it is a double that can be one of these values: +Infinitity, -Infinity, NaN, or a discrete value representable in IEEE double spec.  There are no signaling NaNs, nor is there any exposure of what the underlying bit pattern of the NaN is.
> 
> So the runtime loads this double, and then stores it somewhere, anywhere, it doesn't matter where, eg.
> var tmp = typedArray[0];
> 
> Now you store it:
> typedArray[whatever] = tmp;
> 
> The specification must allow a bitwise comparison of typedArray[whatever] to typedArray[0] to return false, as it is not possible for any NaN-boxing engine to maintain the bit equality that you would otherwise desire, as that would be trivially exploitable.  When I say security and correctness i mean it in the "can't be remotely pwned" sense.
> 
> Given that we can't guarantee that the bit pattern will remain unchanged the spec should mandate normalizing to the non-signalling NaN.
> 
> --Oliver

Oliver,

Let's look at actual ES6 spec. language and see if there is actually anything you would like to see changed.  The encoding of NaNs is only discussed in three places:

Section 8.1.5, first paragraph says:  

The Number type has exactly 18437736874454810627 (that is, 264253+3) values, representing the double-precision 64-bit format IEEE 754 values as specified in the IEEE Standard for Binary Floating-Point Arithmetic, except that the 9007199254740990 (that is, 2532) distinct “Not-a-Number” values of the IEEE Standard are represented in ECMAScript as a single special NaN value. (Note that the NaN value is produced by the program expression NaN.) In some implementations, external code might be able to detect a difference between various Not-a-Number values, but such behaviour is implementation-dependent; to ECMAScript code, all NaN values are indistinguishable from each other.

This is very old text, most of which dates to ES1. It is defining the ES Number type, which is an abstraction, not an actual bit-level encoding. To me it say these things:
   1) Unlike the the IEEE 64-bit floating point type.  The ES Number type has only a single NaN value. 
   2) An implementations are free to internally encode the Number type any why it desires (as long is it observably conforms to all the requirements of section 8.1.5).
   3) In particular, an implementation  might have multiple internal bit patterns all of which correspond to the single NaN element of the Number type.  
   4) A corollary of 3) is that implementation are not required to internally canonicalize NaNs, that is an implementation level design decision.
   5) Implementations are not required to canonicalize NaNs when they are passed or otherwise made visible to non-ES code.  Hence such code may be able to observe details of the NaN encoding, including whether or not a canonical NaN value is used internally by the ES implementation.

Is there anything you think should change in the above specification text?

Section 15.13.5.1.3 defines the GetValueFromBuffer abstraction operation which is currently the only place in the ES6 spec. where a ES Number value is retrieved from an ArrayBuffer. Step 8 of the specification algorithm is:
8	If type is “Float64” , then
	rawValue is interpreted, taking into accont the value of isBigEndian,  as a bit string encoding of an IEEE 754-208 binary64 value.
	If rawValue is any an IEEE 754-208 binary64 NaN value, return the NaN Number value.
	Return the Number value that is encoded by rawValue.
Step 7 is similar but deals with Float32 values.

To me it say these things:
   1) In all cases, an ES Number value (as defined in 8.1.5) is returned.
   2) All IEEE NaN values are logically canonicalized as the single ES NaN value.
   3) No additional requirements for ES Numbers, beyond those in 8.1.5 are introduced.  Actual representation remains up to implementation.

Should anything change? If 8.1.5 continues to not mandate any particular or single encoding representation of a NaN, I don't see why this should either.

Section 15.13.5.1.4 defines the SetValueInBuffer abstraction operation which is currently the only place in the ES6 spec. where a ES Number value is stored into a ArrayBuffer. Step 8 of the specification algorithm is:

8.	Else, if type is “Float64” , then
	Set rawValue to the 8 bytes that are the IEEE-868-2005 binary64 format encoding of value. If isBigEndian is true, the bytes are arranged in big endian order.  Otherwise, the bytes are arranged in little endian order.  If value is NaN, rawValue is may be set to any implementation choosen non-signaling NaN encoding.
Step 7 is similar but deals with Float32 values.

To me it say these things:
   1) When storing into an ArrayBuffer, canonicalization of ES NaN to some particular IEEE NaN is permitted but not required.
   2) The actual ArrayBuffer bit-level representation of NaN values is completely implementation dependent.  It need not even be consistent across multiple stores. .
   3) External observers of such a stored value may be able to detect NaN encoding differences by observing Numbers stored into ArrayBuffers.  This is allowed by 8.1.5
   4) The actual encoding of a NaN value in an ArrayBuffer is observable by ES code by overly a non-float typed Array on an ArrayBuffer where a Number has been stored using the above steps.

Point 4 seems to be in conflict with the 8.1.5 requirement "to ECMAScript code, all NaN values are indistinguishable from each other.".  However, consider that what is being observed is not directly an ES Number NaN value but instead a bit pattern that according to15.13.5.1.4 above may not be a direct reflection of the Number NaN that was stored. So maybe it slips by.

So, again any change requests?

From a spec. perspective we could require canonicalization to some specific IEEE NaN (how do we pick one?) in 15.13.5.4.  If we are going to do that, we presumably also should require canonicalization of endian-ness which is also observable and arguably a bigger interop hazard than NaNs.  However, I think the performance arguments for not canonicalizing endian-ness in typed array are stronger.  If we are going to continue to  let endian-ness be implementation determined then I don't think there is much point in worrying about changing the the handling of NaNs in 15.13.5.1.4. But, I'm flexible.

Allen







-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20130326/0f704bb6/attachment-0001.html>


More information about the es-discuss mailing list