Real bugs found in Sakai and related bug pattern
Introduction
Location of daily bug pattern reports against trunk (findbugs only):
- Patterns that have already generated Jira's - http://qa1-nl.sakaiproject.org/codereview/bug_dashboard/index_generic.html
- Performance - http://qa1-nl.sakaiproject.org/codereview/bug_dashboard/findbugs_PERFORMANCE.html
- Multi threaded - http://qa1-nl.sakaiproject.org/codereview/bug_dashboard/findbugs_MT_CORRECTNESS.html
- Bad practice - http://qa1-nl.sakaiproject.org/codereview/bug_dashboard/findbugs_BAD_PRACTICE.html
- Correctness - http://qa1-nl.sakaiproject.org/codereview/bug_dashboard/findbugs_CORRECTNESS.html
- Style - http://qa1-nl.sakaiproject.org/codereview/bug_dashboard/findbugs_STYLE.html
- Malicious - http://qa1-nl.sakaiproject.org/codereview/bug_dashboard/findbugs_MALICIOUS_CODE.html
Location of per project report including qj pro and PMD results and mentioning duplicate code http://qa1-nl.sakaiproject.org/codereview/trunk/
Bug Parade
Findbugs
Findbugs have currently 220 or so rules, below are a list of examples against rule type and if possible Jira
Rule: IS2_INCONSISTENT_SYNC (Inconsistent synchronization) SAK-13269
Thread1 calls doWork() and it obtains a lock on the object so no other thread can concurrently access code guarded by this lock. Thread2 tries to call reset() and it is allowed to proceed since it is not guarded by the lock held by Thread1. T1 has just checked that field b is not null. It will try to dereference b.toString() but in the meantime T2 has cleared b(by calling reset()) and T1 will throw a NullPointerException even though it has just tried to prevent just that from happening.
Possible solutions:
- shrink the synchronized block and make a defensive copy of b.
- synchronize all accesses of b too.
The book 'Java concurrency in practice' is a good starting point for a more thorough explanation.
public class Demo { private StringBuilder a = new StringBuilder(); private StringBuilder b = new StringBuilder(); public void reset() { b = null; } public synchronized String doWork() { if (b != null) { a.append(b.toString()); } return a.toString(); } }
Rule: IL_INFINITE_LOOP (Potential infinite loop) SAK-12916
207 int nr = fin.read(buffer, 0, bsize); 208 OutputStream out = res.getOutputStream(); 209 while (nr > 0) 210 { 211 out.write(buffer, 0, nr) 212 }
Rule: IL_INFINITE_RECURSIVE_LOOP
38 public boolean equal(Object o){ 39 return this.equal(o); 40 }
Rule: ES_COMPARING_STRINGS_WITH_EQ SAK-12947
274 if ((_defaultCatalog != null) && (conn.getCatalog() != _defaultCatalog)) 275 { 276 conn.setCatalog(_defaultCatalog); 277 }
Rule: PMD PlaceLiteral http://jira.sakaiproject.org/jira/browse/SAK-13291
there are 928 places in Sakai with the following bug pattern:
if(sortColumn.equals("title")) {
if sortColumn is null then a nullpointer exception is generated. Defensive programming practice suggests that it should be
"title".equals then if sortColumn is null then false is returned.
Better still you could concentrate your strings at the top of the class to make the code easier to read.
See:
http://qa1-nl.sakaiproject.org/codereview/bug_dashboard/pmd_literals_first.html
Hint:
The TFTP project in Eclipse includes does static code review based on > 70 rules. After analysis, Eclipse can quick fix this bug pattern.
See: http://www.eclipse.org/projects/project_summary.php?projectid=tptp.platform
Rule: RV_RETURN_VALUE_IGNORED SAK-12979
value.replaceAll returns the replaced string, but does not replace the string in value itself.
517 public static String unEscapeHtml(String value) 518 { 519 if (value == null) return ""; 520 if (value.equals("")) return ""; 521 value.replaceAll("<", "<"); 522 value.replaceAll(">", ">"); 523 value.replaceAll("&", "&"); 524 value.replaceAll(""", "\""); 525 return value 526 }
Rule: FE_TEST_IF_EQUAL_TO_NOT_A_NUMBER SAK-12944
Details:special semantics of NaN, no value is equal to Nan, including NaN.
1302 if (!(studentAnswerNum == Float.NaN || answer1Num == Float.NaN|| answer2Num == Float.NaN)){ 1303 matchresult=((answer1Num <= studentAnswerNum) && (answer2Num >= studentAnswerNum)) 1304 }
Rule: NP_ALWAYS_NULL SAK-12948
lines 8992,8996 both notAddedOfficialAccounts ,notAddedNonOfficialAccounts are null
8984 // update the not added user list 8985 String notAddedOfficialAccounts = null; 8986 String notAddedNonOfficialAccounts = null; 8987 for (Iterator iEIds = eIdRoles.keySet().iterator(); iEIds.hasNext();) { 8988 String iEId = (String) iEIds.next(); 8989 if (!addedParticipantEIds.contains(iEId)) { 8990 if (iEId.indexOf(EMAIL_CHAR) == -1) { 8991 // no email in eid 8992 notAddedOfficialAccounts = notAddedOfficialAccounts 8993 .concat(iEId + "\n"); 8994 } else { 8995 // email in eid 8996 notAddedNonOfficialAccounts = notAddedNonOfficialAccounts 8997 .concat(iEId + "\n"); 8998 } 8999 } 9000 }
Rule: STCAL_STATIC_SIMPLE_DATE_FORMAT_INSTANCE SAK-13068
As the JavaDoc states, DateFormats are inherently unsafe for multithreaded use. Sharing a single instance across thread boundaries without proper synchronization will result in erratic behavior of the application.
You may also experience serialization problems.
Using an instance field is recommended.
For more information on this see Sun Bug #6231579 and Sun Bug #6178997.
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6231579
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6178997
Rule: SBSC_USE_STRINGBUFFER_CONCATENATION, WMI_WRONG_MAP_ITERATOR SAK-13039
Two patterns:
1. Inefficient iterations
2. String concatenation
Updated nightly - http://qa1-nl.sakaiproject.org/codereview/bug_dashboard/findbugs_generic_performance_it.html
See: http://javaantipatterns.wordpress.com/2007/11/22/accessing-the-map-values-using-keyset-iterator
1. Inefficient iterations
A common mistake is to retrieve values from a Map while iterating over the Map keys with keySet(). Calling Map.get(key) for each entry is expensive and should be avoided for better performance.
Use entrySet() iterator to avoid the Map.get(key) lookup.
//Bad: for (Object key: map.keySet()) doSomething(map.get(key)); //Good: for (Map.Entry entry: map.entrySet()) doSomething(entry.getValue());
2. Poor string concatenation - can lead to a cost quadratic in the number of iterations
(SBSC_USE_STRINGBUFFER_CONCATENATION)
The method seems to be building a String using concatenation in a loop. In each iteration, the String is converted to a StringBuffer/StringBuilder, appended to, and converted back to a String. This can lead to a cost quadratic in the number of iterations, as the growing string is recopied in each iteration.
Better performance can be obtained by using a StringBuffer (or StringBuilder in Java 1.5) explicitly.
For example:
// This is bad String s = ""; for (int i = 0; i < field.length; ++i) { s = s + field[i]; } // This is better StringBuffer buf = new StringBuffer(); for (int i = 0; i < field.length; ++i) { buf.append(field[i]); } String s = buf.toString();