InBetween
InBetween

Reputation: 32750

GetType() can lie?

Based on the following question asked a few days ago in SO: GetType() and polymorphism and reading Eric Lippert's answer, I started thinking if making GetType() not be virtual really ensured that an object could not lie about its Type.

Specifically, Eric's answer states the following:

The framework designers are not going to add an incredibly dangerous feature such as allowing an object to lie about its type merely to make it consistent with three other methods on the same type.

Now the question is: can I make an object that does lie about its type without it being immediately obvious? I may be profoundly wrong here and I'd love clarification if that is the case, but consider the following code:

public interface IFoo
{
    Type GetType();
}

And the following two implementations of said interface:

public class BadFoo : IFoo
{
    Type IFoo.GetType()
    {
        return typeof(int);
    }
}

public class NiceFoo : IFoo
{
}

Then if you run the following simple program:

static void Main(string[] args)
{
    IFoo badFoo = new BadFoo();
    IFoo niceFoo = new NiceFoo();
    Console.WriteLine("BadFoo says he's a '{0}'", badFoo.GetType().ToString());
    Console.WriteLine("NiceFoo says he's a '{0}'", niceFoo.GetType().ToString());
    Console.ReadLine();
}

Sure enough badFoo outputs an erroneous Type.

Now I don't know if this has any serious implications based on Eric describing this behavior as an "incredibly dangerous feature", but could this pattern pose a credible threat?

Upvotes: 95

Views: 3723

Answers (8)

Vlad
Vlad

Reputation: 35594

Well, actually there is already a type which can lie in GetType: any nullable type.

This code:

int? x = 0; int y = 0;
Console.WriteLine(x.GetType() == y.GetType());

outputs True.


Actually, it's not the int? who is lying, just implicit cast to object turns int? into a boxed int. But nevertheless you cannot tell int? from int with GetType().

Upvotes: 7

Johannes Wanzek
Johannes Wanzek

Reputation: 2875

There are two ways of being sure about the Type:

  1. Use typeof on the Type which can't be overloaded

    IFoo badFoo = new BadFoo();
    IFoo niceFoo = new NiceFoo();
    
    Console.WriteLine("BadFoo says he's a '{0}'", badFoo.GetType().ToString());
    Console.WriteLine("NiceFoo says he's a '{0}'", niceFoo.GetType().ToString());
    
    Console.WriteLine("BadFoo really is a '{0}'", typeof(BadFoo));
    Console.WriteLine("NiceFoo really is a '{0}'", typeof(NiceFoo));
    Console.ReadLine();
    
  2. Cast the instance to an object and call the GetType() Method

    IFoo badFoo = new BadFoo();
    IFoo niceFoo = new NiceFoo();
    
    Console.WriteLine("BadFoo says he's a '{0}'", badFoo.GetType().ToString());
    Console.WriteLine("NiceFoo says he's a '{0}'", niceFoo.GetType().ToString());
    
    Console.WriteLine("BadFoo really is a '{0}'", ((object)badFoo).GetType());
    Console.WriteLine("NiceFoo really is a '{0}'", ((object)niceFoo).GetType());
    Console.ReadLine();
    

Upvotes: 32

Paolo Falabella
Paolo Falabella

Reputation: 25844

Nice question! The way I see it, you could only really mislead a fellow developer if GetType was virtual on object, which it isn't.

What you did is akin to shadowing GetType, like this:

public class BadFoo
{
    public new Type GetType()
    {
        return typeof(int);
    }
}

with this class (and using the sample code from the MSDN for the GetType() method) you could indeed have:

int n1 = 12;
BadFoo foo = new BadFoo();

Console.WriteLine("n1 and n2 are the same type: {0}",
                  Object.ReferenceEquals(n1.GetType(), foo.GetType())); 
// output: 
// n1 and n2 are the same type: True

so, yikes, you've successfully lied, right? Well, yes and no... Consider that using this as an exploit would mean using your BadFoo instance as an argument to a method somewhere, that expects likely an object or a common base type for a hierarchy of objects. Something like this:

public void CheckIfInt(object ob)
{
    if(ob.GetType() == typeof(int))
    {
        Console.WriteLine("got an int! Initiate destruction of Universe!");
    }
    else
    {
        Console.WriteLine("not an int");
    }
}

but CheckIfInt(foo) prints "not an int".

So, basically (back to your example), you could really only exploit your "lying type" with code that someone wrote against your IFoo interface, which is very explicit about the fact that it has a "custom" GetType() method.

Only if GetType() was virtual on object you would be able to craft a "lying" type that could be used with methods like CheckIfInt above to create havoc in libraries written by someone else.

Upvotes: 45

huysentruitw
huysentruitw

Reputation: 28111

You have some options if you want to play safe against that kind of hack:

Cast to object first

You can call the original GetType() method by first casting the instance to an object:

 Console.WriteLine("BadFoo says he's a '{0}'", ((object)badFoo).GetType());

results in:

BadFoo says he's a 'ConsoleApplication.BadFoo'

Use template method

Using this template method will also give you the real type:

static Type GetType<T>(T obj)
{
    return obj.GetType();
}

GetType(badFoo);

Upvotes: 3

M&#229;rten Wikstr&#246;m
M&#229;rten Wikstr&#246;m

Reputation: 11344

No, you can't make GetType lie. You are only introducing a new method. Only code that are aware of this method will call it.

You cannot for example make third-party or framework code call your new GetType method instead of the real one, since that code does not know that your method exists and will therefore never call it.

You can however confuse your own developers with such a declaration. Any code that is compiled with your declaration and that use parameters or variables typed as IFoo or any type derived from that will indeed use your new method instead. But since that only affects your own code it does not really impose a "threat".

If you do want to provide a custom type description for a class this should be done using a Custom Type Descriptor, perhaps by annotating your class with a TypeDescriptionProviderAttribute. This can be useful in some situations.

Upvotes: 10

bpoiss
bpoiss

Reputation: 14003

There is a difference between object.GetType and IFoo.GetType. GetType is called at compile time on not known objects, an not on Interfaces. In your example, with output badFoo.GetType it is expected bahaviour, because you overload the method. Only thing is, that other programmers can get confused of this behaviour.

But if you use typeof() it will output that the type is the same, and you can't overwrite typeof().

Also the programmer can see at compile time, which method GetType he invokes.

So to your question: This pattern can not pose a credible threat, but it is also not best style of coding.

Upvotes: 2

Moeri
Moeri

Reputation: 9294

I don't think it will, since every library code that calls GetType will declare the variable as 'Object' or as a Generic type 'T'

The following code:

    public static void Main(string[] args)
    {
        IFoo badFoo = new BadFoo();
        IFoo niceFoo = new NiceFoo();
        PrintObjectType("BadFoo", badFoo);
        PrintObjectType("NiceFoo", niceFoo);
        PrintGenericType("BadFoo", badFoo);
        PrintGenericType("NiceFoo", niceFoo);
    }

    public static void PrintObjectType(string actualName, object instance)
    {
        Console.WriteLine("Object {0} says he's a '{1}'", actualName, instance.GetType());
    }

    public static void PrintGenericType<T>(string actualName, T instance)
    {
        Console.WriteLine("Generic Type {0} says he's a '{1}'", actualName, instance.GetType());
    }

prints:

Object BadFoo says he's a 'TypeConcept.BadFoo'

Object NiceFoo says he's a 'TypeConcept.NiceFoo'

Generic Type BadFoo says he's a 'TypeConcept.BadFoo'

Generic Type NiceFoo says he's a 'TypeConcept.NiceFoo'

The only time this kind of code will result in bad scenario's is in your own code, where you declare the parameter type as IFoo

    public static void Main(string[] args)
    {
        IFoo badFoo = new BadFoo();
        IFoo niceFoo = new NiceFoo();
        PrintIFoo("BadFoo", badFoo);
        PrintIFoo("NiceFoo", niceFoo);
    }

    public static void PrintIFoo(string actualName, IFoo instance)
    {
        Console.WriteLine("IFoo {0} says he's a '{1}'", actualName, instance.GetType());
    }

IFoo BadFoo says he's a 'System.Int32'

IFoo NiceFoo says he's a 'TypeConcept.NiceFoo'

Upvotes: 5

Shadow Wizard
Shadow Wizard

Reputation: 66389

Worst that can happen as far as I can tell is misleading innocent programmers who happen to use the poisoned class, for example:

Type type = myInstance.GetType();
string fullName = type.FullName;
string output;
if (fullName.Contains(".Web"))
{
    output = "this is webby";
}
else if (fullName.Contains(".Customer"))
{
    output = "this is customer related class";
}
else
{
    output = "unknown class";
}

If myInstance is instance of a class such as you describe in the question, it will just be treated as unknown type.

So my answer is no, can't see any real threat here.

Upvotes: 4

Related Questions