-
Notifications
You must be signed in to change notification settings - Fork 2k
Description
Jetty version(s)
9.4.58
but current version should also be affected
Jetty Environment
Sessionhandling
HTTP version
doesn't matter
Java version/vendor (use: java -version)
OpenJDK Runtime Environment (Temurin)(build 1.8.0_422-b05)
OpenJDK 64-Bit Server VM (Temurin)(build 25.422-b05, mixed mode)
but doesn't matter
OS type/version
Linux
Description
There is a race-condition in AbstractSessionCache. This results in HttpServletRequest.getSession(false) returning 'null' although there is a valid Session. This happens only if passivation in combination with evicition is used. The problem is much more likely to happen if isSaveOnInactiveEviction() == true and you have a slow storage-device and 'big' objects in the Session-Attributes (meaning serialization of data to disc will take much time).
If HttpServletRequest.getSession(false) is called, while serialization of session to disc is running, it will wait for serialization to finish but then return null. Expected behavior would be, that the session will be activated again and then returned.
Everything works fine, if getSession is called, after writing to disc has finished.
This happens due to a race between the worker thread accessing AbstractSessionCache.getAndEnter() and the eviction-thread calling AbstractSessionCache.checkInactiveSession(). The eviction-thread will lock the session, then writing the data to disc. Since writing is slow, there is a big chance for the other thread in getAndEnter() to call doComputeIfAbsent and to get the same session-object the eviction-thread is working on, since this will be removed from the map after it was written to disc. The thread calling getAndEnter() will than try to lock this session-object an therefor wait till the otherone has finished. It then checks is isResident() returns true. But this is not the case. Now logmessage 'Non-resident session in cache' is printed and method returns null. this is wrong.
the simplest way to fix it would be, to double-check in such cases.
Using this custom Session-Cache-implementation fixes this problem instantly:
SessionCache cache = new DefaultSessionCache(handler) {
protected Session getAndEnter(String id, boolean enter) throws Exception {
Session session = super.getAndEnter(id, enter);
if (session == null) {
// possibly stale session detected.
// try again:
session = super.getAndEnter(id, enter);
}
return session;
}
};
Please be aware, that just modifying checkInactiveSession and removing the session from the map before writing to disc wont fix the problem - it just makes it more unlikely to happen.
How to reproduce?
- write a simple Servlet that (when called) calls
getSession(true) - if a session was created, put a serializable object into the attributes. you can implement a custom
private void writeObject(java.io.ObjectOutputStream out) throws IOException- Method with an integratedThread.sleep()to get a fool-safe result ;-) - don't forget to add some log-messages
- setup your jetty using passivation and eviction. enable SaveOnInactiveEviction
- startup and call your servlet through a webbrowser
- wait for your log-message, that serialization is in progress
- while eviction-thread is sleeping at
Thread.sleep()refresh your browser. - getSession() will return null although the session is just evicted, not timed out...
i only tried with jetty 9.4.58 but source of current jetty 12 doesn't seem to differ in this part...
Thanks and greetz,
Karsten
Metadata
Metadata
Assignees
Labels
Type
Projects
Status