New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
LCK08-J. Ensure actively held locks are released on exceptional conditions #2055
base: master
Are you sure you want to change the base?
Changes from 7 commits
834fec3
2d35194
230349d
c6c41ae
fdd7d2a
c7b3043
e035202
737b27f
9dcc723
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package edu.umd.cs.findbugs.detect; | ||
|
||
import edu.umd.cs.findbugs.AbstractIntegrationTest; | ||
import edu.umd.cs.findbugs.test.matcher.BugInstanceMatcher; | ||
import edu.umd.cs.findbugs.test.matcher.BugInstanceMatcherBuilder; | ||
import org.junit.Test; | ||
|
||
import static edu.umd.cs.findbugs.test.CountMatcher.containsExactly; | ||
import static org.hamcrest.CoreMatchers.hasItem; | ||
import static org.hamcrest.MatcherAssert.assertThat; | ||
|
||
public class FindUnreleasedLockTest extends AbstractIntegrationTest { | ||
|
||
@Test | ||
public void doesNotFindSSDBug_ifLockIsClosedInAProperWay() { | ||
performAnalysis("unreleasedLock/ProperlyClosedLock.class"); | ||
|
||
assertAnalyzedClassHasNoBugs(); | ||
} | ||
|
||
@Test | ||
public void findPossibleUnlockCall_withoutLockCall() { | ||
//SystemProperties.setProperty("ful.debug", "true"); | ||
gonczmisi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
performAnalysis("unreleasedLock/ThrowExceptionAndUnlockBeforeLock.class"); | ||
|
||
assertAnalyzedClassHasBug("CWO_CLOSED_WITHOUT_OPENED", 1); | ||
assertExactPlaceOfBug("ThrowExceptionAndUnlockBeforeLock", "doSomething", "CWO_CLOSED_WITHOUT_OPENED"); | ||
} | ||
|
||
@Test | ||
public void findUnresolvedLock_onExceptionalCondition() { | ||
performAnalysis("unreleasedLock/UnreleasedLock.class"); | ||
|
||
assertAnalyzedClassHasBug("UL_UNRELEASED_LOCK_EXCEPTION_PATH", 1); | ||
assertExactPlaceOfBug("UnreleasedLock", "doSomething", "UL_UNRELEASED_LOCK_EXCEPTION_PATH"); | ||
} | ||
|
||
private void assertAnalyzedClassHasNoBugs() { | ||
final BugInstanceMatcher bugTypeMatcher = new BugInstanceMatcherBuilder() | ||
.build(); | ||
assertThat(getBugCollection(), containsExactly(0, bugTypeMatcher)); | ||
} | ||
|
||
private void assertAnalyzedClassHasBug(String bugType, int numberOfBugs) { | ||
final BugInstanceMatcher bugTypeMatcher = new BugInstanceMatcherBuilder() | ||
.bugType(bugType) | ||
.build(); | ||
assertThat(getBugCollection(), containsExactly(numberOfBugs, bugTypeMatcher)); | ||
} | ||
|
||
private void assertExactPlaceOfBug(String className, String methodName, String bugType) { | ||
final BugInstanceMatcher bugInstanceMatcher = new BugInstanceMatcherBuilder() | ||
.bugType(bugType) | ||
.inClass(className) | ||
.inMethod(methodName) | ||
.build(); | ||
assertThat(getBugCollection(), hasItem(bugInstanceMatcher)); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1054,13 +1054,14 @@ Returning a zero length array is generally preferred in this context to returnin | |
</Detector> | ||
<Detector class="edu.umd.cs.findbugs.detect.FindUnreleasedLock"> | ||
<Details> | ||
<![CDATA[ | ||
<p> This detector looks for JSR-166 (<code>java.util.concurrent</code>) | ||
locks which are acquired, but not released on all paths out of the method. | ||
It is a moderately fast detector. Note that in order to use this | ||
detector, you need to have the <code>java.util.concurrent</code> package | ||
in the auxiliary classpath (or be analyzing the package itself).</p> | ||
]]> | ||
<![CDATA[ | ||
<p> This detector looks for JSR-166 (<code>java.util.concurrent</code>) | ||
locks which are acquired, but not released on all paths out of the method. | ||
The detector also detects if a lock is released without even being opened in the first place. | ||
It is a moderately fast detector. Note that in order to use this | ||
detector, you need to have the <code>java.util.concurrent</code> package | ||
in the auxiliary classpath (or be analyzing the package itself).</p> | ||
]]> | ||
</Details> | ||
</Detector> | ||
<Detector class="edu.umd.cs.findbugs.detect.FindRefComparison"> | ||
|
@@ -8785,6 +8786,18 @@ Using floating-point variables should not be used as loop counters, as they are | |
</p>]]> | ||
</Details> | ||
</BugPattern> | ||
<BugPattern type="CWO_CLOSED_WITHOUT_OPENED"> | ||
<ShortDescription>Method releases lock without opening it</ShortDescription> | ||
<LongDescription>{1} releases lock without acquiring it in the first place</LongDescription> | ||
<Details> | ||
<![CDATA[ | ||
<p> This method releases a JSR-166 (<code>java.util.concurrent</code>) lock, | ||
but it is possible that the lock is not even acquired at this point, | ||
due to an exception thrower method before the locking. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please add a statement about what the programmer should do, when this bug occurs? |
||
</p> | ||
]]> | ||
</Details> | ||
</BugPattern> | ||
<!-- | ||
********************************************************************** | ||
BugCodes | ||
|
@@ -8938,4 +8951,5 @@ Using floating-point variables should not be used as loop counters, as they are | |
<BugCode abbrev="THROWS">Exception throwing related problems</BugCode> | ||
<BugCode abbrev="PERM">Custom class loader does not call its superclass's getPermissions()</BugCode> | ||
<BugCode abbrev="USC">Potential security check based on untrusted source</BugCode> | ||
<BugCode abbrev="CWO">Lock closed without being opened in the first place</BugCode> | ||
</MessageCollection> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,20 +37,31 @@ public class ResourceValueFrame extends Frame<ResourceValue> { | |
*/ | ||
public static final int OPEN_ON_EXCEPTION_PATH = 2; | ||
|
||
/** | ||
* The resource is closed without being opened in the first place. | ||
*/ | ||
public static final int CLOSED_WITHOUT_OPENED = 3; | ||
|
||
/** | ||
* The resource is closed (or unlocked, etc). | ||
*/ | ||
public static final int CLOSED = 3; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. better to keep public APIs compatible. Can we move new constants after the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately no, because the mergeing of these states are based on a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @KengoTODA could you revisit this PR and check my comment above? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then it is a breaking change, and I think only should be merged for 5.0.0, together with #2069, which may cause some merge conflicts with this one. Also, it would be worth to mention the breaking change in the changelog as well. |
||
public static final int CLOSED = 4; | ||
|
||
/** | ||
* The resource has been created, but is not open. | ||
*/ | ||
public static final int CREATED = 4; | ||
public static final int CREATED = 5; | ||
|
||
/** | ||
* The resource doesn't exist. | ||
*/ | ||
public static final int NONEXISTENT = 5; | ||
public static final int NONEXISTENT = 6; | ||
|
||
/** | ||
* The resource is NOT open (or locked, etc) on paths that include exception | ||
* control flow. | ||
*/ | ||
public static final int NOT_OPEN_ON_EXCEPTION_PATH = 7; | ||
|
||
private int status; | ||
|
||
|
@@ -84,8 +95,8 @@ public void copyFrom(Frame<ResourceValue> other_) { | |
this.status = other.status; | ||
} | ||
|
||
private static final String[] statusList = { "(escaped)", "(open)", "(open_exception)", "(closed)", "(created)", | ||
"(nonexistent)" }; | ||
private static final String[] statusList = { "(escaped)", "(open)", "(open_exception)", "(closed_without_opened)", "(closed)", "(created)", | ||
"(nonexistent)", "(not_open_exception)" }; | ||
|
||
@Override | ||
public String toString() { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is already an
Added
section in Unreleased. Can you please merge the two?