CVE-2014-8122: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') (CWE-362)
value: Race condition in JBoss Weld before 2.2.8 and 3.x before 3.0.0 Alpha3 allows remote attackers to obtain information from a previous conversation via vectors related to a stale thread state.
CWE description: The product contains a code sequence that can run concurrently with other code, and the code sequence requires temporary, exclusive access to a shared resource, but a timing window exists in which the shared resource can be modified by another code sequence that is operating concurrently.
Published: 2015-02-13T15:59Z
Repository: https://github.com/weld/core
Language: Java
Files changed: tests-arquillian/src/test/java/org/jboss/weld/tests/contexts/cache/ConversationScopedBean.java, tests-arquillian/src/test/java/org/jboss/weld/tests/contexts/cache/RequestScopedCacheLeakTest.java, tests-arquillian/src/test/java/org/jboss/weld/tests/contexts/cache/SimpleServlet.java
Commit date: 2014-12-04 11:16:55 +0100
Commit message: WELD-1802 RequestScopedCache - Testcase

Diff: diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/contexts/cache/ConversationScopedBean.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/contexts/cache/ConversationScopedBean.java
new file mode 100644
index 000000000..bd4f8c2b7
--- /dev/null
+++ b/tests-arquillian/src/test/java/org/jboss/weld/tests/contexts/cache/ConversationScopedBean.java
@@ -0,0 +1,33 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2014, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.weld.tests.contexts.cache;
+
+import java.io.Serializable;
+
+import javax.enterprise.context.ConversationScoped;
+
+@ConversationScoped
+public class ConversationScopedBean implements Serializable {
+
+    private String value = "foo";
+
+    public String getAndSet(String newValue) {
+        String old = value;
+        value = newValue;
+        return old;
+    }
+}
diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/contexts/cache/RequestScopedCacheLeakTest.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/contexts/cache/RequestScopedCacheLeakTest.java
new file mode 100644
index 000000000..bba9b820e
--- /dev/null
+++ b/tests-arquillian/src/test/java/org/jboss/weld/tests/contexts/cache/RequestScopedCacheLeakTest.java
@@ -0,0 +1,80 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2014, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.weld.tests.contexts.cache;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import junit.framework.Assert;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.EmptyAsset;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.jboss.weld.tests.category.Integration;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
+import com.gargoylesoftware.htmlunit.WebClient;
+
+@RunWith(Arquillian.class)
+@Category(Integration.class)
+public class RequestScopedCacheLeakTest {
+
+    @ArquillianResource
+    private URL contextPath;
+
+    @Deployment(testable = false)
+    public static WebArchive getDeployment() {
+        return ShrinkWrap.create(WebArchive.class).addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
+                .addClasses(SimpleServlet.class, ConversationScopedBean.class);
+    }
+
+    @Test
+    public void test() throws Exception {
+        WebClient webClient = new WebClient();
+        webClient.setThrowExceptionOnFailingStatusCode(false);
+        for (int i = 0; i < 100; i++) {
+            // first, send out a hundred of poisoning requests
+            // each of these should leave a thread in a broken state
+            sendRequest(webClient, i, true);
+        }
+        for (int i = 0; i < 100; i++) {
+            // now send out normal requests to see if they are affected by the thread's broken state
+            String result = sendRequest(webClient, i, false);
+            Assert.assertFalse("Invalid state detected after " + (i + 1) + " requests", result.startsWith("bar"));
+        }
+    }
+
+    private String sendRequest(WebClient webClient, int sequence, boolean poison) throws FailingHttpStatusCodeException, MalformedURLException, IOException {
+        final String path = getPath("getAndSet", sequence, poison);
+        return webClient.getPage(path).getWebResponse().getContentAsString().trim();
+    }
+
+    private String getPath(String test, int sequence, boolean poison) {
+        String path = contextPath + "/servlet?action=" + test + "&sequence=" + sequence;
+        if (poison) {
+            path += "&poison=true";
+        }
+        return path;
+    }
+}
diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/contexts/cache/SimpleServlet.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/contexts/cache/SimpleServlet.java
new file mode 100644
index 000000000..93a114a14
--- /dev/null
+++ b/tests-arquillian/src/test/java/org/jboss/weld/tests/contexts/cache/SimpleServlet.java
@@ -0,0 +1,55 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2014, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.weld.tests.contexts.cache;
+
+import java.io.IOException;
+
+import javax.inject.Inject;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.jboss.weld.context.beanstore.ConversationNamingScheme;
+
+@WebServlet(value = "/servlet", asyncSupported = true)
+public class SimpleServlet extends HttpServlet {
+
+    @Inject
+    private ConversationScopedBean bean;
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        String action = req.getParameter("action");
+        String sequence = req.getParameter("sequence");
+        String poison = req.getParameter("poison");
+        if ("getAndSet".equals(action)) {
+            // the value should always be foo
+            String value = bean.getAndSet("bar" + sequence);
+            resp.getWriter().println(value);
+            if (poison != null) {
+                // this is a poisoning request
+                // normal applications should never do something like this
+                // we just do this to cause an exception to be thrown out of ConversationContext.deactivate
+                req.removeAttribute(ConversationNamingScheme.PARAMETER_NAME);
+            }
+        } else {
+            throw new IllegalArgumentException(action);
+        }
+    }
+}

Fixed code: /*
 * JBoss, Home of Professional Open Source
 * Copyright 2014, Red Hat, Inc., and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.weld.tests.contexts.cache;

import java.io.Serializable;

import javax.enterprise.context.ConversationScoped;

@ConversationScoped
public class ConversationScopedBean implements Serializable {

    private String value = "foo";

    public String getAndSet(String newValue) {
        String old = value;
        value = newValue;
        return old;
    }
}
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2014, Red Hat, Inc., and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.weld.tests.contexts.cache;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import junit.framework.Assert;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jboss.weld.tests.category.Integration;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;

import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.WebClient;

@RunWith(Arquillian.class)
@Category(Integration.class)
public class RequestScopedCacheLeakTest {

    @ArquillianResource
    private URL contextPath;

    @Deployment(testable = false)
    public static WebArchive getDeployment() {
        return ShrinkWrap.create(WebArchive.class).addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
                .addClasses(SimpleServlet.class, ConversationScopedBean.class);
    }

    @Test
    public void test() throws Exception {
        WebClient webClient = new WebClient();
        webClient.setThrowExceptionOnFailingStatusCode(false);
        for (int i = 0; i < 100; i++) {
            // first, send out a hundred of poisoning requests
            // each of these should leave a thread in a broken state
            sendRequest(webClient, i, true);
        }
        for (int i = 0; i < 100; i++) {
            // now send out normal requests to see if they are affected by the thread's broken state
            String result = sendRequest(webClient, i, false);
            Assert.assertFalse("Invalid state detected after " + (i + 1) + " requests", result.startsWith("bar"));
        }
    }

    private String sendRequest(WebClient webClient, int sequence, boolean poison) throws FailingHttpStatusCodeException, MalformedURLException, IOException {
        final String path = getPath("getAndSet", sequence, poison);
        return webClient.getPage(path).getWebResponse().getContentAsString().trim();
    }

    private String getPath(String test, int sequence, boolean poison) {
        String path = contextPath + "/servlet?action=" + test + "&sequence=" + sequence;
        if (poison) {
            path += "&poison=true";
        }
        return path;
    }
}
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2014, Red Hat, Inc., and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.weld.tests.contexts.cache;

import java.io.IOException;

import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.jboss.weld.context.beanstore.ConversationNamingScheme;

@WebServlet(value = "/servlet", asyncSupported = true)
public class SimpleServlet extends HttpServlet {

    @Inject
    private ConversationScopedBean bean;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String action = req.getParameter("action");
        String sequence = req.getParameter("sequence");
        String poison = req.getParameter("poison");
        if ("getAndSet".equals(action)) {
            // the value should always be foo
            String value = bean.getAndSet("bar" + sequence);
            resp.getWriter().println(value);
            if (poison != null) {
                // this is a poisoning request
                // normal applications should never do something like this
                // we just do this to cause an exception to be thrown out of ConversationContext.deactivate
                req.removeAttribute(ConversationNamingScheme.PARAMETER_NAME);
            }
        } else {
            throw new IllegalArgumentException(action);
        }
    }
}