[Bug 38988] .NET applications using System.Decimal to float conversion may return wrong results ('VarR4FromDec' divisor integer overflow)

wine-bugs at winehq.org wine-bugs at winehq.org
Wed Jul 29 14:31:15 CDT 2015


https://bugs.winehq.org/show_bug.cgi?id=38988

Anastasius Focht <focht at gmx.net> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |dotnet
             Status|UNCONFIRMED                 |NEW
                 CC|                            |focht at gmx.net
          Component|-unknown                    |oleaut32
            Summary|when using MS .net there    |.NET applications using
                   |are wrong results (but no   |System.Decimal to float
                   |errors)                     |conversion may return wrong
                   |                            |results ('VarR4FromDec'
                   |                            |divisor integer overflow)
     Ever confirmed|0                           |1

--- Comment #4 from Anastasius Focht <focht at gmx.net> ---
Hello folks,

confirming.

Prerequisite: 'winetricks -q dotnet40'

IL code of the app using 'ILSpy':

--- snip ---
.method private hidebysig static
    void Main (
        string[] args
    ) cil managed
{
    // Method begins at RVA 0x2050
    // Code size 161 (0xa1)
    .maxstack 6
    .entrypoint
    .locals init (
        [0] valuetype [mscorlib]System.Decimal,
        [1] float32
    )

    IL_0000: ldc.i4 1353919093
    IL_0005: ldc.i4 138
    IL_000a: ldc.i4.0
    IL_000b: ldc.i4 128
    IL_0010: ldc.i4.s 12
    IL_0012: newobj instance void [mscorlib]System.Decimal::.ctor(int32, int32,
int32, bool, uint8)
    IL_0017: stloc.0
    IL_0018: ldloc.0
    IL_0019: call float32 [mscorlib]System.Decimal::op_Explicit(valuetype
[mscorlib]System.Decimal)
    IL_001e: conv.r4
    IL_001f: stloc.1
    IL_0020: ldstr "Value as decimal: "
    IL_0025: ldloc.0
    IL_0026: box [mscorlib]System.Decimal
    IL_002b: call string [mscorlib]System.String::Concat(object, object)
    IL_0030: call void [mscorlib]System.Console::WriteLine(string)
    IL_0035: ldstr "Value converted to float: "
    IL_003a: ldloc.1
    IL_003b: box [mscorlib]System.Single
    IL_0040: call string [mscorlib]System.String::Concat(object, object)
    IL_0045: call void [mscorlib]System.Console::WriteLine(string)
    IL_004a: ldc.i4 -268880975
    IL_004f: ldc.i4 140
    IL_0054: ldc.i4.0
    IL_0055: ldc.i4.0
    IL_0056: ldc.i4.s 12
    IL_0058: newobj instance void [mscorlib]System.Decimal::.ctor(int32, int32,
int32, bool, uint8)
    IL_005d: stloc.0
    IL_005e: ldloc.0
    IL_005f: call float32 [mscorlib]System.Decimal::op_Explicit(valuetype
[mscorlib]System.Decimal)
    IL_0064: conv.r4
    IL_0065: stloc.1
    IL_0066: ldstr "Value as decimal: "
    IL_006b: ldloc.0
    IL_006c: box [mscorlib]System.Decimal
    IL_0071: call string [mscorlib]System.String::Concat(object, object)
    IL_0076: call void [mscorlib]System.Console::WriteLine(string)
    IL_007b: ldstr "Value converted to float: "
    IL_0080: ldloc.1
    IL_0081: box [mscorlib]System.Single
    IL_0086: call string [mscorlib]System.String::Concat(object, object)
    IL_008b: call void [mscorlib]System.Console::WriteLine(string)
    IL_0090: ldstr "\n<RETURN>"
    IL_0095: call void [mscorlib]System.Console::WriteLine(string)
    IL_009a: call string [mscorlib]System.Console::ReadLine()
    IL_009f: pop
    IL_00a0: ret
} // end of method Program::Main 
--- snip ---

'Decimal' constructor '(Int32, Int32, Int32, Boolean, Byte)':

https://msdn.microsoft.com/en-us/library/bb1c1a6x.aspx

--- quote ---
public Decimal(
    int lo,
    int mid,
    int hi,
    bool isNegative,
    byte scale
)
...
Parameters

lo
    Type: System.Int32

    The low 32 bits of a 96-bit integer. 

mid
    Type: System.Int32

    The middle 32 bits of a 96-bit integer. 

hi
    Type: System.Int32

    The high 32 bits of a 96-bit integer. 

isNegative
    Type: System.Boolean

    true to indicate a negative number; false to indicate a positive number. 

scale
    Type: System.Byte

    A power of 10 ranging from 0 to 28. 

...

Remarks

The binary representation of a Decimal number consists of a 1-bit sign, a
96-bit integer number, and a scaling factor used to divide the integer number
and specify what portion of it is a decimal fraction. The scaling factor is
implicitly the number 10 raised to an exponent ranging from 0 to 28.
--- quote ---

The first number -0.594059405941:

--- snip ---
IL_0000: ldc.i4 1353919093
IL_0005: ldc.i4 138
IL_000a: ldc.i4.0
IL_000b: ldc.i4 128
IL_0010: ldc.i4.s 12
--- snip ---

Using 'bc' command line tool to check the compiler did the right thing on
emitting DECIMAL parts for '-0.594059405941m':

--- snip ---
$ echo "scale=20; -(138*2^32+1353919093)/10^12" | bc
-.59405940594100000000
--- snip ---

Yep.

--- snip ---
$ Wine-dbg>info locals

0x7e679c3f VarR4FromDec+0x3d: (0033f228)
    DECIMAL* pDecIn=0x33f23c (parameter [EBP+8])
    float* pFltOut=0x33f238 (parameter [EBP+12])
    BYTE scale=12 (local [ESP+39])
    int divisor=0x1 (local [ESP+32])
    double highPart=...(local [ESP+24])

Wine-dbg>p *pDecIn
{wReserved=0, u={s={scale=12, sign=-128}, signscale=0x800c}, Hi32=0,
u1={s1={Lo32=0x50b32a75, Mid32=0x8a}, Lo64=0x8a50b32a75}}
--- snip ---

Source:
https://source.winehq.org/git/wine.git/blob/bedd444a3616fea54a060e07c27a137f29052fc9:/dlls/oleaut32/vartype.c#l2948

--- snip ---
2948 HRESULT WINAPI VarR4FromDec(DECIMAL* pDecIn, float *pFltOut)
2949 {
2950   BYTE scale = DEC_SCALE(pDecIn);
2951   int divisor = 1;
2952   double highPart;
2953 
2954   if (scale > DEC_MAX_SCALE || DEC_SIGN(pDecIn) & ~DECIMAL_NEG)
2955     return E_INVALIDARG;
2956 
2957   while (scale--)
2958     divisor *= 10;
2959 
2960   if (DEC_SIGN(pDecIn))
2961     divisor = -divisor;
2962 
2963   if (DEC_HI32(pDecIn))
2964   {
2965     highPart = (double)DEC_HI32(pDecIn) / (double)divisor;
2966     highPart *= 4294967296.0F;
2967     highPart *= 4294967296.0F;
2968   }
2969   else
2970     highPart = 0.0;
2971 
2972   *pFltOut = (double)DEC_LO64(pDecIn) / (double)divisor + highPart;
2973   return S_OK;
2974 }
--- snip ---

Integer overflow for divisor. The data type must cover a range of at least
-1e+28..1e+28 here.

$ wine --version
wine-1.7.48

Regards

-- 
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.



More information about the wine-bugs mailing list