2

Is it possible for a caller of the following method to craft a string reference and pass it as token such that the method returns true? (Assuming the caller does not get the string reference from the static field using reflection.)

class GuessTheSecret
{
    private static readonly string Secret = new string("Secret".ToCharArray());

    public static bool IsCorrect(string token)
    {
        return object.ReferenceEquals(token, Secret);
    }
}
dtb
  • 213,145
  • 36
  • 401
  • 431
  • Bah, missed your disclaimer ;) – MerickOWA Sep 17 '11 at 17:06
  • If the compiler/jitter of some future version manages to optimize out your initializer to a constant then it's easily possible. Or if it just decides to intern it after initializing the static variable. Else I see no way. – CodesInChaos Sep 17 '11 at 17:08
  • @CodeInChaos readonly to constant optimization is unlikely. http://stackoverflow.com/questions/1456785/a-definite-guide-to-api-breaking-changes-in-net – Icarus Sep 17 '11 at 17:28
  • The code is vulnerable to a simple brute force attack. Shouldn't take more than a few milliseconds. – Hans Passant Sep 17 '11 at 17:30
  • @Hans: I'm not really concerned about elaborate attacks (using reflection is easier), but I'm curious: how can you craft references in .NET and test if one happens to be the reference you're looking for? – dtb Sep 17 '11 at 17:35
  • interning static readonly string fields would be possible though. And the Jitter is perfectly within its right to do that optimization. – CodesInChaos Sep 17 '11 at 17:38
  • Create an object that's big enough, use a pointer to mess with it. – Hans Passant Sep 17 '11 at 17:39

2 Answers2

2

No. You'd have to expose the static instance to the caller. Reference equality means that it is exactly the same object. Unless there is a way to access that particular object, you can't have another that references the same memory. If you were to use the equality operator, that would be different as string overloads that to do value equality rather than reference equality.

Had you, on the other hand, set the static instance value to a constant, you could (by using the constant) have a reference that is equal to the static instance. That is because string literals are interned and shared through out the code, meaning that all string literals that are the same have the same reference.

For example,

class GuessTheSecret
{
    private static readonly string Secret = "Secret";

    public static bool IsCorrect(string token)
    {
        return object.ReferenceEquals(token, Secret);
    }
}

Console.WriteLine( GuessTheSecret.IsCorrect( "Secret" ) );

would output True

tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • 2
    What if the CLR decides to intern my secret string for some reason? – dtb Sep 17 '11 at 17:08
  • @dtb - I would consider it a bug if this were to happen when you explicitly use `new`. In that case you, the programmer, are asking for a new reference. Compiler writers shouldn't try to outguess the programmer in this case. – tvanfosson Sep 17 '11 at 17:16
  • In case of `new string('a', 0)` the CLR does exactly that: It returns a reference to the interned string `""` instead of creating a new string object of zero length and returning a reference to it. – dtb Sep 17 '11 at 17:18
  • @dtb - reading what Jon Skeet has to say about it, I would assume that if you want anything other than a literal to be interned, you have to ask for it: http://www.yoda.arachsys.com/csharp/strings.html – tvanfosson Sep 17 '11 at 17:20
  • @dtb: The empty string appears to be a special case as documented [here](http://msdn.microsoft.com/en-us/library/system.string.intern.aspx). Experiment with non-empty strings, like your secret string, and see if anything wacky goes on. The same MSDN page says interning is only done automatically for literals, otherwise `string.Intern()` on demand. – BoltClock Sep 17 '11 at 17:21
  • @dtb - ok, so it handles the corner case of the empty string in an exceptional manner. It probably also handles null the same way. That wasn't your example, however. – tvanfosson Sep 17 '11 at 17:21
0

I believe is "it depends", basically on whether the caller is passing a same-assembly string that was interned with your string.

Here's why:

ReferenceEquals does what you think, comparing memory locations and not object value (MSDN):

By calling the ReferenceEquals method, you can see that the two strings actually refer to the same object in memory.

There's a C# feature called string interning which according to Eric Lippert:

If you have two identical string literals in one compilation unit then the code we generate ensures that only one string object is created by the CLR for all instances of that literal within the assembly. This optimization is called "string interning".

However, from the same post, interning by default is not guaranteed for each and every string:

If you were writing a compiler in C#, or had some other application in which you felt that it was worth your while to ensure that thousands of identical strings do not consume lots of memory, you can force the runtime to intern your string with the String.Intern method.

Conversely, if you hate interning with an unreasoning passion, you can force the runtime to turn off all string interning in an assembly with the CompilationRelaxation attribute.

Whatever you're trying to accomplish, this current implementation you have seems unreliable at best- I'd consider rethinking what you're doing.

Community
  • 1
  • 1
Factor Mystic
  • 26,279
  • 16
  • 79
  • 95
  • So the literal "Secret" is interned. I can intern `new string("Secret".ToCharArray())` if I want. I can disable interning assembly-wide. So far, so good. The question that remains: Can it happen that `new string("Secret".ToCharArray())` is automatically interned for some reason such that the `Secret` field references the interned string? – dtb Sep 17 '11 at 17:17
  • considering who's asking the question I think this is out of educational curiosity only and not for production code – BrokenGlass Sep 17 '11 at 17:18