To Thine Own Self Be True

By Michael R.

While working on a .NET 2.0 web application, the team encountered a bug of a particularly maddening sort (often described as a Heisenbug).  The issue can be reproduced reliably, but only when the source is compiled and run in release mode. With debug-mode compilation, or stepping through a release-mode compile in Visual Studio, the code performs as expected.

Essentially, when the release-mode code executes outside of Visual Studio, requesting the index of a known item in a list returns -1, indicating no matching object was found in the list.  IndexOf uses equality between the argument and each object in the list to determine if item is in the list.  In this instance, the object is there, but the test for equality failed.

The details of using the debugger and the “Immediate window” to identify the culprit are fodder for future discussion.  The bug itself, which is recreated in the console application below, shows why it failed, along with a demonstration of the fix. Some readers may notice that several details of implementing the IComparable interface are missing, which were omitted for brevity.

using System;

 

namespace FloatIssue

{

class Program

    {

static void Main(string[] args)

        {

MyFloat mf0 = new MyFloat(0f);

Console.WriteLine(“MyFloat(0f).Equals(self)      : {0}”, mf0.Equals(mf0));

 

MyFloat mf1 = new MyFloat(0.01f);

Console.WriteLine(“MyFloat(0.01f).Equals(self)   : {0}”, mf1.Equals(mf1));

 

Console.WriteLine(“MyFloat(0f).EqualsFix(self)   : {0}”, mf0.EqualsFix(mf0));

Console.WriteLine(“MyFloat(0.01f).EqualsFix(self): {0}”, mf1.EqualsFix(mf1));

        }

    }

 

class MyFloat

    {

private readonly float _DisplayValue;

 

public MyFloat(float pDisplayValue)

        {

            _DisplayValue = pDisplayValue;

        }

 

public float Value

        {

get

            {

return _DisplayValue / 100;

            }

        }

 

public float DisplayValue

        {

get

            {

return _DisplayValue;

            }

        }

 

public bool Equals(MyFloat pFloatBug)

        {

return (Value == pFloatBug.Value);

        }

 

public bool EqualsFix(MyFloat pFloatFix)

        {

return (DisplayValue == pFloatFix.DisplayValue);

        }

 

    }

} 

The value object class is a composition of several other value object classes, so its equality test depends on the equality tests for its members.  One member, which implements a percentage-weighted value, is represented by the MyFloat class.  The parameters of the constructor include a display value.  The display value is retrieved using the DisplayValue property, and the derived Value is calculated and returned by the getter each time the property is accessed.  The Equals method tests equality of the (derived) Value properties.

In debug mode, the output of the application is:

MyFloat(0f).Equals(self)   : True

MyFloat(0.01f).Equals(self): True

MyFloat(0f).EqualsFix(self)   : True

MyFloat(0.01f).EqualsFix(self): True

 

However, in release mode, executed from the command line, the output is:

MyFloat(0f).Equals(self)      : True

MyFloat(0.01f).Equals(self)   : False

MyFloat(0f).EqualsFix(self)   : True

MyFloat(0.01f).EqualsFix(self): True

A small change to the Equals method (shown as EqualsFix) to compare the readonly DisplayValue attribute rather than the derived-on-demand Value property causes the class to behave as expected in both modes.

—Michael R.

About Dan Cornell

A globally recognized application security expert, Dan Cornell holds over 15 years of experience architecting, developing and securing web-based software systems. As the Chief Technology Officer and a Principal at Denim Group, Ltd., he leads the technology team to help Fortune 500 companies and government organizations integrate security throughout the development process. He is also the original creator of ThreadFix, Denim Group's industry leading application vulnerability management platform.
More Posts by Dan Cornell

Categories: Information Security

2 Responses to “To Thine Own Self Be True”

  1. Erhan J. Kartaltepe

    @Bill:

    We suspect it is. We are, as they say, running more tests (as well as adding similar QA checks for other systems). :-)

    Thanks for the University of Utah link. It looks like there are some conversions to Java. Could a transliteration to C# be far behind?

Leave a Reply

Your email address will not be published. Required fields are marked *