Thanks to Jason Whittington and Peter Drayton for helping me figure this out.
I was playing around with versioning in the CLR today, and I started to wonder what would happen under the following scenario:
Someone writes a component - call it Foo - and deploys another component - say A - that uses it. They also have a client that uses A. Later, they version Foo to v2, and build component B against it. Then they add code to the client to work with B. Both A and B have methods that accept a Foo as a parameter, but A has been compiled against Foo v1, and B has been compiled against Foo v2. The client has been updated to work with the latest Foo v2.
Here's the picture that shows what everyone is built against
Now, the terrible part comes when the client creates a Foo and passes it to A. A thinks it's getting a Foo v1, but it's really getting a Foo v2. And the truly awful bit is, it acts as if it were a Foo v1, right down to going after the wrong bits of memory and calling the wrong methods.
Click here to download some code that demonstrates this. Simply extract the files into some directory, run nmake to build everything, and then run client. Observe the output, and tell me if you think that's what you'd expect based on looking at client.cs.
Here's another variation that actually crashes the process. That's usually (incorrectly) thought to be impossible without writing unsafe or unmanaged code.
The sort-of good news is that the code won't verify. This doesn't help when you run it with full trust (since fully trusted code gets to skip verification) but at least someone can't exploit this issue for mobile code.
The reason this happens is that C# (and VB.NET, and others) give you no way to specify which version of something you'd like to build against at a language level. This totally sucks - there are times when you really, really care what winds up in the metadata, even with config files. We can, of course, fix this particular problem by using interfaces that we don't version, but I would argue that being forced into a design decision to avoid a language limitation is sub-optimal.
No comments:
Post a Comment