Java Forum / General / September 2006
Why is String immutable?
dimitrik107@hotmail.com - 11 Sep 2006 14:42 GMT Why String is immutable? Is there simple explanation I can't find it?
Fred Kleinschmidt - 11 Sep 2006 15:53 GMT > Why String is immutable? Is there simple explanation I can't find it? For the same reason Integer is immutable: that's how the author(s) decided to do it.
You could also ask: Why isn't a StringBuffer immutable?
 Signature Fred L. Kleinschmidt Boeing Associate Technical Fellow Technical Architect, Software Reuse Project
Chris Smith - 11 Sep 2006 16:16 GMT > Why String is immutable? Is there simple explanation I can't find it? There are at least two reasons.
The first is a design principle. Immutable classes for basic types like String make designs much more comprehensible. If you pass a String around the application, you don't need to worry about who else has a reference to it, who might modify it without your realizing the possibility, and so forth. Especially in a heavily multithreaded environment like Java, that's the only way that it's safe to share a String throughout the application.
The second reason has to do with the Java security model. When a security policy is installed, certain restrictions can be enforced on -- for example -- which files can be read, which hosts can be connected to over the network, and so on. The immutable String class ensures that security-sensitive APIs only have to check the file name or host name once, and can then rely on it to stay the same. A mutable String class would introduce a race condition where the application (in another thread) could modify the file name after the security check, but before the file is actually opened, and thus circumvent the security mechanism.
If you think about it, you'll notice that when seen from some perspectives, the second is a specialization on the first reason.
 Signature Chris Smith
Matt Rose - 11 Sep 2006 17:45 GMT > The second reason has to do with the Java security model. When a > security policy is installed, certain restrictions can be enforced on -- [quoted text clipped - 5 lines] > thread) could modify the file name after the security check, but before > the file is actually opened, and thus circumvent the security mechanism. Hi, I wholeheartedly agree with your first point of it being the only sane way to implement Strings from a design point of view, but I'm less sure about relying on this for security. The underlying char[] is still writable if you try a bit harder. I expect the method below could be forbidden with the right security policy (ReflectPermission seems to be granted by default on my system) but I suspect you could still access the field directly if you craft your own byte code?
import java.lang.reflect.Field;
public class StringImmutabilityTest {
public static void main(String[] args) throws Exception { String fileNameToServe = "/ftp/readme"; char[] injection = "/etc/passwd".toCharArray(); Field f = fileNameToServe.getClass().getDeclaredField("value"); f.setAccessible(true); char[] val = (char[]) f.get(fileNameToServe); System.arraycopy(injection, 0, val, 0, injection.length); System.out.println(fileNameToServe); } }
Of course, you're probably doomed the moment you allow untrusted code into your VM anyway!
By the way, calling new String(String) on any untrusted Strings will probably keep you a little safe from this.
Matt
Matt Rose - 11 Sep 2006 18:09 GMT > > The second reason has to do with the Java security model. When a > > security policy is installed, certain restrictions can be enforced on -- [quoted text clipped - 36 lines] > > Matt Hmm, I take it back about new String(String) helping. This contructor only copies the char[] if original String had some wasted space, e.g. it was the product of a subString().
You'd need to duplicate the char[] yourself and wrap that in a String if you're worried about malicious people having references to your Strings.
Matt
Chris Smith - 12 Sep 2006 03:19 GMT > Hi, I wholeheartedly agree with your first point of it being the only > sane way to implement Strings from a design point of view, but I'm less [quoted text clipped - 3 lines] > seems to be granted by default on my system) but I suspect you could > still access the field directly if you craft your own byte code? Everything is granted by default. The point is that if you install a security manager, you can control this stuff. There are some permissions that can never be granted to security-sensitive code; but you just don't grant those permissions. One of those is ReflectPermission("suppressAccessChecks"), which covers the call to setAccessible.
> Of course, you're probably doomed the moment you allow untrusted code > into your VM anyway! Not at all. That's what applets do, millions of times per day. If you were right, then someone could really cause havoc around the world. You're not right, though.
 Signature Chris Smith
Matt Rose - 12 Sep 2006 12:47 GMT > > Hi, I wholeheartedly agree with your first point of it being the only > > sane way to implement Strings from a design point of view, but I'm less [quoted text clipped - 10 lines] > ReflectPermission("suppressAccessChecks"), which covers the call to > setAccessible. Does this get checked by the GETFIELD opcode? I can't find any indication that it does in the VM spec but I haven't tested it. I never normally get involved with bytecode.
> > Of course, you're probably doomed the moment you allow untrusted code > > into your VM anyway! > > Not at all. That's what applets do, millions of times per day. If you > were right, then someone could really cause havoc around the world. > You're not right, though. Excellent point, well made. It's easy to forget java is used on the client side, too.
Matt
Chris Smith - 12 Sep 2006 16:55 GMT > Does this get checked by the GETFIELD opcode? I can't find any > indication that it does in the VM spec but I haven't tested it. I never > normally get involved with bytecode. The getfield opcode is checked by the bytecode verifier when the class is loaded. If it tries to access a field to which it doesn't have access permission, the verifier will throw a java.lang.VerifyError, and the class will fail to load.
This has nothing to do with ReflectPermission("suppressAccessChecks"); it just is. If you want to suppress access checks, you can't do it with direct bytecode instructions to access the hidden methods and fields. Even if you were to call setAccessible, you still couldn't load bytecode that tries to access a private field directly. You would need to use the Reflection API to do that.
As long as we're on the topic, it should be mentioned that the discussion of private fields might be misleading. From a security standpoint, private is actually considerably less interesting than package-access restrictions. The Java compiler routinely generates hidden package-access methods that allow access to private fields (particularly when dealing with nested classes), so relying on private access is not safe. Building a secure API requires placing classes in a sealed package, and then ensuring that all protected or public access meets the security requirements. Since the core API packages are sealed, this is exactly what happens here.
 Signature Chris Smith
Babu Kalakrishnan - 12 Sep 2006 18:49 GMT > As long as we're on the topic, it should be mentioned that the > discussion of private fields might be misleading. From a security [quoted text clipped - 6 lines] > meets the security requirements. Since the core API packages are > sealed, this is exactly what happens here. One query in connection with that last statement. Remember seeing an article a while back about how easy it is to crack an encrypted classloader by replacing one of the classes in rt.jar with a modified version (the ClassLoader class). If the rt.jar file is indeed sealed, would this be allowed ? (Isn't there some restriction that every class in a package must be signed by the same signer?)
Also, is there a way for an application program to determine if it is indeed running under a JVM whose core library is sealed ? (I remember trying to call the getSigners() method on ClassLoader.class to check this out, but if my memory serves me right, it returned null in a JDK1.4 runtime).
The article I am referring to is :
http://www.javaworld.com/javaworld/javaqa/2003-05/01-qa-0509-jcrypt.html
BK
Chris Smith - 13 Sep 2006 05:59 GMT > One query in connection with that last statement. Remember seeing an > article a while back about how easy it is to crack an encrypted > classloader by replacing one of the classes in rt.jar with a modified > version (the ClassLoader class). If the rt.jar file is indeed sealed, > would this be allowed ? It is allowed because you are assuming the ability to replace rt.jar. All of the system packages in rt.jar are sealed, but you can replace rt.jar with a different JAR file where those packages are not sealed. Hostile code running within the JVM would not have the ability to replace the rt.jar file.
The article you quote also mentions JVMPI, which is since superceded by JVMTI. These APIs also work, but again are not available to hostile code running under a SecurityManager.
> (Isn't there some restriction that every class > in a package must be signed by the same signer?) Nope. All that's required is that the line "Sealed: true" in the JAR manifest. Package signing is a different matter. If the JVM loads a class from a JAR file with "Sealed: true" in the manifest, then it will ensure that it does not (and has not, in the past) load a class in the same package from a different JAR file.
> Also, is there a way for an application program to determine if it is > indeed running under a JVM whose core library is sealed? If you have a Class object, you can call myClass.getPackage().isSealed() to find out. I don't know of a place where that doesn't work. The API docs are missing @since tags (infuriatingly common), but I'm sure it's been around for at least a couple major versions now (1.4 and 1.5, at a minimum; possibly as old as 1.2).
 Signature Chris Smith
Babu Kalakrishnan - 13 Sep 2006 07:45 GMT >>(Isn't there some restriction that every class >>in a package must be signed by the same signer?) [quoted text clipped - 4 lines] > ensure that it does not (and has not, in the past) load a class in the > same package from a different JAR file. Thanks, I was confused between "Sealed" and "Signed".
Interestingly, I just looked at the manifest.mf file present in the rt.jar of my JDK distribution - and it doesn't even seem to have a "Sealed: true" attribute present.
Is this because the java.* package hierarchy is "sealed" through extraneous means (by allowing loading only through the bootstrap classloader) and Sun wants users to be able to replace files in the javax hierarchy ?
BK
Thomas Hawtin - 13 Sep 2006 08:04 GMT >> (Isn't there some restriction that every class >> in a package must be signed by the same signer?) [quoted text clipped - 4 lines] > ensure that it does not (and has not, in the past) load a class in the > same package from a different JAR file. Assuming a package is not sealed, you can have other class loaders load classes into the same package namespace. However, those classes will not have package access rights to the original package. A class loader also does not allow classes to be defined in the same package (with the same class loader) with different certificates.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Chris Uppal - 13 Sep 2006 12:28 GMT "Chris Smith" <cdsmith@twu.net> wrote in message
> The getfield opcode is checked by the bytecode verifier when the class > is loaded. If it tries to access a field to which it doesn't have > access permission, the verifier will throw a java.lang.VerifyError, and > the class will fail to load. The picture is more complicated than that. For a start, I don't think that it's the verifier's job to enforce access checks -- they shouldn't fail until an illegal access is actually executed.
Secondly, the rules for when the JVM will actually enforce access seem to be somewhat obscure. I know that it will /sometimes/ do checks, but not always. And what "sometimes" means has changed over the years.
For instance. With JDK 1.5.0_06, compile these two files
========== A.java ================= public class A { // NB: deliberately public, despite the name public String privateField = "Ooops!"; } =========== B.java ================ public class B { public static void main(String[] args) { A a = new A(); System.out.println("OK, here we go..."); System.out.println(a.privateField); } } =================================
Then change the declaration of A.privateField to private, and recompile only A.java. (All this messing around is just a way to generate bytecode containing an "illegal" read -- people with convenient access to bytecode generation will have more straightforward ways of doing it).
Then execute:
java -cp . B
and it prints:
OK, here we go... Ooops!
OTOH, running java with the "future" argument:
java -Xfuture -cp . B
and it prints:
OK, here we go... Exception in thread "main" java.lang.IllegalAccessError: tried to access field A.privateField from class B at B.main(B.java:8)
On the third hand, even without the -Xfuture argument, the current JVM will (iirc) stop you accessing the internal fields of a String object. I'm not sure on what basis it determines that some accesses should be checked and others not.
As far as I know, /all/ such access should result in the JVM throwing runtime exceptions, but Sun's JVM's seem to interpret that aspect of the spec a little loosely...
(And anyway, there's always JNI ;-)
-- chris
Matt Humphrey - 11 Sep 2006 16:20 GMT > Why String is immutable? Is there simple explanation I can't find it? I've seen a number of reasons, although I don't know for certain which of these the original designers had these in mind:
1) To put Strings on par with Floats, Integers, etc as value objects specified literally and which cannot be modified by method side-effect. 2) So strings can be easily shared (rather than copying content) 3) So strings can be interned (and tested for equality via ==) 4) They're inherently threadsafe 5) To let them work cleanly as keys in maps without the possibility of changing 6) To simplify learning the object model 7) So their hashcodes do not have to be recomputed 8) StringBuffer is perfectly good at managing mutable Strings
And, of course, depending on your Security Manager, it is possible (but never a good idea) to change a String's contents via reflection.
Some results from Google:
http://www.acooke.org/andrew/immutable.html
http://www.programmersheaven.com/2/FAQ-JAVA-String-Is-Immutable
http://en.wikipedia.org/wiki/Immutable_object
Matt Humphrey matth@ivizNOSPAM.com http://www.iviz.com/
Tor Iver Wilhelmsen - 11 Sep 2006 16:41 GMT > Why String is immutable? Is there simple explanation I can't find it? Safety (no changing under the covers), efficiency (substring is just a pointer to the same char array with different indices), reliability as a hash value. At least.
Arne Vajhøj - 11 Sep 2006 17:04 GMT > Why String is immutable? Is there simple explanation I can't find it? Somebody made that choice.
Maybe the following can give you some ideas about why:
http://www.javaworld.com/javaworld/javaqa/2000-06/01-qa-0602-immutable.html http://www.artima.com/intv/gosling313.html
Arne
Mark Rafn - 11 Sep 2006 19:44 GMT >Why String is immutable? Is there simple explanation I can't find it? Don't forget to ask the important further question "when I design a class, should I make it immutable?". IMO, the idea of immutable data objects is far underutilized.
Immutability does a lot of things that aren't obvious at first glance, and you probably want more of your classes to have these properties:
- sets and maps behave consistently. Mutable objects can go in and later not be findable because their equals() and hashCode() are different. - clarity of intent. if your object is immutable, you never have to worry they'll muck with it when you pass it into someone else's code. Likewise, when they return an immutable object, you don't have to ask yourself whether you need to check for value changes or set listeners on properties before actually using the object. - thread safety. -- Mark Rafn dagon@dagon.net <http://www.dagon.net/>
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|