Oh what a tangled web we weave when first to thread we do conceive.
Administrivia Remember: still time to vote today Q2 back today P3 M1 today and tomorrow All of you have arranged times (good) Remember to think about sched for future meetings (incl. rollout) All group members should be present if at all possible Teleconference can be arranged if you need...
A Study of History Last time: Civics advice Definitions for threading & synchronization “Who owns the lock”, part I Today: WOTL, part II Design principle Multi-processing, security, & you: race conditions
WOTL: Setup public class WhoOwnsTheLock { private List x=new LinkedList(); private static List y=new LinkedList(); private int a=3; private static int b=42; }
WOTL: Setup The memory picture:
Who owns the lock? public class WhoOwnsTheLock { private List x=new LinkedList(); private static List y=new LinkedList(); private int a=3; private static int b=42; public synchronized List getY0() { return y; } }
Who owns the lock? public class WhoOwnsTheLock { private List x=new LinkedList(); private static List y=new LinkedList(); private int a=3; private static int b=42; public synchronized List getY0() { return y; } } Again, “ this ” owns the lock. Danger Will Robinson! Can have multiple access to y !!!
Who owns the lock? Consider: WhoOwnsTheLock foo=new WhoOwnsTheLock(); WhoOwnsTheLock bar=new WhoOwnsTheLock(); // in thread 0 foo.getY0(); // in thread 1 bar.getY0();
Who owns the lock?
thread 0 synch w/ this lock & thread 1 synch w/ this one
Who owns the lock? public class WhoOwnsTheLock { private List x=new LinkedList(); private static List y=new LinkedList(); private int a=3; private static int b=42; public synchronized List getY0() { return y; } public static synchronized List getY1() { return y; }
Who owns the lock? public class WhoOwnsTheLock { private List x=new LinkedList(); private static List y=new LinkedList(); private int a=3; private static int b=42; public synchronized List getY0() { return y; } public static synchronized List getY1() { return y; } Remember: “ static ”==“associated with class” (i.e., class object). So this is synchronized on the class object. No more conflict. (yay!)
Who owns the lock?
public class WhoOwnsTheLock { private List x=new LinkedList(); private static List y=new LinkedList(); private int a=3; private static int b=42; public synchronized List getY0() { return y; } public static synchronized List getY1() { return y; } public static List getY2() { synchronized(y) { return y; } }
Who owns the lock? public class WhoOwnsTheLock { private List x=new LinkedList(); private static List y=new LinkedList(); private int a=3; private static int b=42; public synchronized List getY0() { return y; } public static synchronized List getY1() { return y; } public static List getY2() { synchronized(y) { return y; } } Effectively, just as good as previous version...
Who owns the lock?
public class WhoOwnsTheLock { private List x=new LinkedList(); private static List y=new LinkedList(); private int a=3; private static int b=42; public synchronized int getXY0() { return x.size()+y.size(); }
Who owns the lock? public class WhoOwnsTheLock { private List x=new LinkedList(); private static List y=new LinkedList(); private int a=3; private static int b=42; public synchronized int getXY0() { return x.size()+y.size(); } Synchronized on “ this ”, but can have concurrent access via y ( x is safe)
Who owns the lock? public class WhoOwnsTheLock { private List x=new LinkedList(); private static List y=new LinkedList(); private int a=3; private static int b=42; public synchronized int getXY0() { return x.size()+y.size(); } public static synchronized int getXY1() { return x.size()+y.size(); }
Who owns the lock? public class WhoOwnsTheLock { private List x=new LinkedList(); private static List y=new LinkedList(); private int a=3; private static int b=42; public synchronized int getXY0() { return x.size()+y.size(); } public static synchronized int getXY1() { return x.size()+y.size(); } Ok, so this is a syntax error. Can’t access x from static context. :-P But pretend for a moment that it would work. What happens?
Who owns the lock? public class WhoOwnsTheLock { private List x=new LinkedList(); private static List y=new LinkedList(); private int a=3; private static int b=42; public synchronized int getXY0() { return x.size()+y.size(); } public static synchronized int getXY1() { return x.size()+y.size(); } Same problem, but in reverse -- y is safe, but can have multi-access to x.
Who owns the lock? public class WhoOwnsTheLock { private List x=new LinkedList(); private static List y=new LinkedList(); private int a=3; private static int b=42; public synchronized int getXY0() { return x.size()+y.size(); } public static synchronized int getXY1() { return x.size()+y.size(); } public int getXY2() { synchronized(this) { // this will work synchronized(y) { return x.size()+y.size(); } } } }
Who owns the lock? public class WhoOwnsTheLock { private List x=new LinkedList(); private static List y=new LinkedList(); private int a=3; private static int b=42; public synchronized int getXY0() { return x.size()+y.size(); } public static synchronized int getXY1() { return x.size()+y.size(); } public int getXY2() { synchronized(x) { // OR you can do this... synchronized(y) { return x.size()+y.size(); } } } }
Who owns the lock? public class WhoOwnsTheLock { private List x=new LinkedList(); private static List y=new LinkedList(); private int a=3; private static int b=42; public synchronized int getXY0() { return x.size()+y.size(); } public static synchronized int getXY1() { return x.size()+y.size(); } public int getXY2() { synchronized(this) { synchronized(this.getClass()) { // OR this... return x.size()+y.size(); } } } }
Who owns the lock? synch(this) synch(y)
Who owns the lock? synch(y) synch(x)
Who owns the lock? synch(this.getClass) synch(this)
Who owns the lock? public class WhoOwnsTheLock { private List x=new LinkedList(); private static List y=new LinkedList(); private int a=3; private static int b=42; public int getAB0() { synchronized(this) { synchronized(b) { // oh oh... b is atomic return a+b; } } } }
Who owns the lock? public class WhoOwnsTheLock { private List x=new LinkedList(); private static List y=new LinkedList(); private int a=3; private static int b=42; public int getAB0() { synchronized(this) { synhronized(y) { return a+b; } } } } Answer 1: synchronize on something “near” b (i.e., something else static) that isn’t, itself, atomic. Sometimes introduce spurious “ Object ” just for this
Design principle o’ the day: Fail fastness
Proactive bug detection Want a bug to show up as soon as possible As close to the creation of the bug as you can Bug should produce (highly) visible evidence Helps debugging -- bug isn’t hidden by many layers of propagation Fail fast principle: design so that bugs cause failures immediately -- as close to source as possible
Fail fast example Use illegal default values when you don’t know the correct final value for a thing private int _arrMax=0; vs. private int _arrMax=-1; Also shows up in java.util.*.iterator() Most built-in JDK iterators are fail fast -- die if you change the collection out from under them
Fail fast iterator public class MyCollection implements Iterable { private Object _data[]; private int _modCount=0; public void add(Object o) { ++_modCount;... } private class _myIter implements Iterator { private int _iterModCount=_modCount; public Object next() { if (_iterModCount!=_modCount) { throw new ConcurrentModificationException(); }... }
Race Conditions & Security
Race Cond. & Security Atomicity failures can sometimes be exploited to break security on multiprocessing systems One of the top 10 classes of exploits since... mid-1980’s, at least 100’s (or more) of reported vulnerabilities Most recently: yesterday Race condition failure reported for mutt mail reader client
The core exploit Privileged program creates a resource Hostile program grabs a shared resource (e.g., file): Before it is created (predicting its name/handle) After it is created, but before it is secured Privileged program
You thought you were safe Independent of language: Java will not save you! Beware when writing privileged code! N.b.: Sometimes your never-intended-to-be- secure code will be run in privileged context! Happens a lot on the web...
Basic Race Cond. Exploit priv proc
Basic Race Cond. Exploit priv proc file /tmp/foo write() read() close() unlink() open(“/tmp/foo”, O_RDWR | O_CREAT);
Basic Race Cond. Exploit priv proc open(“/tmp/foo”, O_RDWR | O_CREAT); file /tmp/foo write() read() close() unlink() hostile proc open(...) read()
Basic Race Cond. Exploit priv proc open(“/tmp/foo”, O_RDWR | O_CREAT); file /tmp/foo write() read() close() unlink() hostile proc chmod()
Basic Race Cond. Exploit priv proc open(“/tmp/foo”, O_RDWR | O_CREAT); file /tmp/foo write() read() close() unlink() hostile proc chmod() open(...)
Basic Race Cond. Exploit priv proc open(“/tmp/foo”, O_RDWR | O_CREAT); file /tmp/foo write() read() close() unlink() hostile proc umask()
Basic Race Cond. Exploit priv proc open(“/tmp/foo”, O_RDWR | O_CREAT); file /tmp/foo write() read() close() unlink() hostile proc umask() open(...) read()
Basic Race Cond. Exploit priv proc open(“/tmp/foo”, O_RDWR | O_CREAT); file /tmp/foo write() read() close() unlink() hostile proc umask() symlink(“/tmp/foo”, “/etc/passwd”)
Basic Race Cond. Exploit priv proc stat(“/tmp/foo”); if (!exists) { open(“/tmp/foo”, O_RDWR | O_CREAT); } else { error(); } file /tmp/foo write() read() close() unlink() hostile proc umask()
Basic Race Cond. Exploit priv proc stat(“/tmp/foo”); if (!exists) { open(“/tmp/foo”, O_RDWR | O_CREAT); } else { error(); } file /tmp/foo write() read() close() unlink() hostile proc umask() symlink(“/tmp/foo”, “/etc/passwd”)
Preventing FS Race Conds Could create foo in dir owned/writable only by owner of proc Can be hard to ensure this Still have to watch out for filename collisions
Preventing FS Race Conds Could make file names hard to predict (e.g., picked randomly) Exploit still possible; hard to make fnames really random Similar “prediction” attack used to break early Netscape implementation of SSL
Preventing FS Race Conds Ultimate answer: use OS atomicity facilities open(“/tmp/foo”, O_RDWR | O_CREAT | O_EXCL) Similar mechanisms used at OS level to ensure atomic access to locks/monitors atomicTestAndSet(), et al. Harder w/ distributed databases -- data lives on multiple hosts DBs usually offer atomic access mechanisms for you Always be on guard!