diff --git a/codan/org.eclipse.cdt.codan.checkers/OSGI-INF/l10n/bundle.properties b/codan/org.eclipse.cdt.codan.checkers/OSGI-INF/l10n/bundle.properties
index cb5aad98f1e..8bc6efd6284 100644
--- a/codan/org.eclipse.cdt.codan.checkers/OSGI-INF/l10n/bundle.properties
+++ b/codan/org.eclipse.cdt.codan.checkers/OSGI-INF/l10n/bundle.properties
@@ -45,6 +45,10 @@ problem.name.UnusedReturnValue = Unused return value
problem.description.NoReturn = No return statement in a function which is declared to return value
problem.messagePattern.NoReturn = No return, in function returning non-void
problem.name.NoReturn = No return
+checker.name.FormatString = Format String
+problem.description.FormatString = Finds statements lead to format string vulnerability (e.g. 'char[5] str; scanf("%10s", str);'
+problem.messagePattern.FormatString = Format string vulnerability in ''{0}''
+problem.name.FormatString = Format String Vulnerability
checker.name.AssignmentToItself = Assignment to itself
problem.messagePattern.AssignmentToItself = Assignment to itself ''{0}''
problem.name.AssignmentToItself = Assignment to itself
diff --git a/codan/org.eclipse.cdt.codan.checkers/plugin.xml b/codan/org.eclipse.cdt.codan.checkers/plugin.xml
index 8ac9b463666..0292066b8d3 100644
--- a/codan/org.eclipse.cdt.codan.checkers/plugin.xml
+++ b/codan/org.eclipse.cdt.codan.checkers/plugin.xml
@@ -320,5 +320,19 @@
name="%checker.name.CaseBreak">
+
scanf
+ * function calls.
+ */
+ private final Pattern vulnerablePattern;
+
+ /**
+ * I guess, this must be a concurrent Collection, but I'm not sure. --
+ * Meisam
+ */
+ private final CollectionARGUMENT_SIZE_NOT_SPECIFIED
, otherwise it returns the number specified after "%". For example:
+ * + * e.g: + *
+ *
+ * int f() {
+ *
+ * char inputstr[5];
+ * scanf("%s", inputstr); // detects vulnerability here
+ * return 0;
+ * }
+ *
+ * e.g: + *
+ *
+ * int f(void) {
+ * char inputstr[5];
+ * int inputval;
+ * int i = 5;
+ * scanf("%d %9s", inputval, inputstr); // detects vulnerability here
+ * printf("%d" ,i);
+ * return 0;
+ * }
+ *
+ *
+ * e.g: + *
+ *
+ * int main(void) {
+ *
+ * @version 0.3 July 29, 2010
+ * @author Meisam Fathi
+ *
+ */
+public class ScanfFormatStringSecurityChecker extends AbstractIndexAstChecker {
+ private static final String ER_ID = "org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem"; //$NON-NLS-1$
+
+ private final static VulnerableFunction[] VULNERABLE_FUNCTIONS = {//
+ // list of all format string vulnerable functions
+ new VulnerableFunction("scanf", 0), //$NON-NLS-1$
+ new VulnerableFunction("fscanf", 1), //$NON-NLS-1$
+ new VulnerableFunction("fwscanf", 1), //$NON-NLS-1$
+ new VulnerableFunction("wscanf", 0), //$NON-NLS-1$
+ new VulnerableFunction("swscanf", 1), //$NON-NLS-1$
+ new VulnerableFunction("sscanf", 1) //$NON-NLS-1$
+ };
+
+ public void processAst(IASTTranslationUnit ast) {
+ ast.accept(new FormatStringVisitor());
+ }
+
+ private static final class VulnerableFunction {
+ private final String name;
+
+ private final int formatStringArgumentIndex;
+
+ private VulnerableFunction(String name, int formatStringArgumentIndex) {
+ this.name = name;
+ this.formatStringArgumentIndex = formatStringArgumentIndex;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return the formatStringArgumentIndex
+ */
+ public int getFormatStringArgumentIndex() {
+ return formatStringArgumentIndex;
+ }
+
+ }
+
+ private class FormatStringVisitor extends ASTVisitor {
+ private FormatStringVisitor() {
+ shouldVisitExpressions = true;
+ }
+
+ public int visit(IASTExpression expression) {
+ if (expression instanceof IASTFunctionCallExpression) {
+ IASTFunctionCallExpression callExpression = (IASTFunctionCallExpression) expression;
+ VulnerableFunction vulnerableFunction = getVulnerableFunctionForExpression(callExpression);
+ if (vulnerableFunction == null) {
+ return PROCESS_CONTINUE;
+ }
+ IASTInitializerClause[] arguments = callExpression
+ .getArguments();
+ int stringArgumentIndex = vulnerableFunction
+ .getFormatStringArgumentIndex();
+
+ detectFaulyArguments(callExpression, arguments,
+ stringArgumentIndex);
+
+ }
+ return PROCESS_CONTINUE;
+ }
+
+ private VulnerableFunction getVulnerableFunctionForExpression(
+ IASTFunctionCallExpression callExpression) {
+ String rawSignature = callExpression.getFunctionNameExpression()
+ .getRawSignature();
+ for (int i = 0; i < VULNERABLE_FUNCTIONS.length; i++) {
+ if (VULNERABLE_FUNCTIONS[i].getName().equals(rawSignature)) {
+ return VULNERABLE_FUNCTIONS[i];
+ }
+ }
+ return null;
+ }
+
+ private void detectFaulyArguments(
+ IASTFunctionCallExpression callExpression,
+ IASTInitializerClause[] arguments, int formatStringArgumentIndex) {
+ final IASTInitializerClause formatArgument = arguments[formatStringArgumentIndex];
+ final String formatArgumentValue = formatArgument.getRawSignature();
+ final CFormatStringParser formatStringParser = new CFormatStringParser(
+ formatArgumentValue);
+
+ if (!formatStringParser.isVulnerable()) {
+ return;
+ }
+
+ // match arguments;
+ final Iterator
+ * char inputstr[5];
+ * int inputval;
+ * int i = 5;
+ * scanf("%4s %i", inputstr, inputval); // no vulnerability here
+ * printf("%d" ,i);
+ * return 0;
+ * }
+ *
+ *
+ */
+ private final int size;
+
+ /**
+ * @param indexOfCurrentArgument
+ * @param group
+ */
+ public VulnerableFormatStringArgument(final int indexOfArgument,
+ final String rgument, final int size) {
+ this.indexOfArgument = indexOfArgument;
+ this.argument = rgument;
+ this.size = size;
+ }
+
+ /**
+ * @return the indexOfArgument
+ */
+ public int getArgumentIndex() {
+ return this.indexOfArgument;
+ }
+
+ /**
+ * @return the argument
+ */
+ public String getArgument() {
+ return this.argument;
+ }
+
+ /**
+ * @return
+ */
+ public int getArgumentSize() {
+ return this.size;
+ }
+
+}
\ No newline at end of file
diff --git a/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/internal/checkers/FormatStringCheckerTest.java b/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/internal/checkers/FormatStringCheckerTest.java
new file mode 100644
index 00000000000..66d4d3a0788
--- /dev/null
+++ b/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/internal/checkers/FormatStringCheckerTest.java
@@ -0,0 +1,267 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Meisam Fathi and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Meisam Fathi - base API
+ *******************************************************************************/
+package org.eclipse.cdt.codan.core.internal.checkers;
+
+import org.eclipse.cdt.codan.core.test.CheckerTestCase;
+
+/**
+ * Test for {@see FormatStringChecker} class
+ *
+ */
+public class FormatStringCheckerTest extends CheckerTestCase {
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ enableProblems("org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem"); //$NON-NLS-1$
+ }
+
+ // int f(){
+ // return 0;
+ // }
+ public void testBase() {
+ loadCodeAndRun(getAboveComment());
+ checkNoErrors();
+ }
+
+ // int f(){
+ // char inputstr[5];
+ // scanf("%s", inputstr); // here
+ // return 0;
+ // }
+ public void testSimple() {
+ loadCodeAndRun(getAboveComment());
+ checkErrorLine(3);
+ }
+
+ // int main(void) {
+ // char inputstr1[5];
+ // int inputval;
+ // int i = 5;
+ // scanf("%i %4s", inputval, inputstr1); // no error here
+ // printf("%d" ,i);
+ // return 0;
+ // }
+ public void testIntRight() {
+ loadCodeAndRun(getAboveComment());
+ checkNoErrors();
+ }
+
+ // int main(void) {
+ // char inputstr[5];
+ // int inputval;
+ // int i = 5;
+ // scanf("%d %9s", inputval, inputstr);
+ // printf("%d" ,i);
+ // return 0;
+ // }
+ public void testIntWrong() {
+ loadCodeAndRun(getAboveComment());
+ checkErrorLine(5);
+ }
+
+ // int main(void) {
+ // char inputstr1[5];
+ // int inputval;
+ // int i = 5;
+ // scanf("%4s %i", inputstr1, inputval);
+ // printf("%d" ,i);
+ // return 0;
+ // }
+ public void testRightInt() {
+ loadCodeAndRun(getAboveComment());
+ checkNoErrors();
+ }
+
+ // int main(void) {
+ // char inputstr1[5];
+ // char inputstr2[5];
+ // int i = 5;
+ // scanf("%4s %9s", inputstr1, inputstr2);
+ // printf("%d" ,i);
+ // return 0;
+ // }
+ public void testRightWrong() {
+ loadCodeAndRun(getAboveComment());
+ checkErrorLine(5);
+ }
+
+ // int main(void) {
+ // char inputstr1[5];
+ // int inputval;
+ // int i = 5;
+ // scanf("%9s %i", inputstr1, inputval);
+ // printf("%d" ,i);
+ // return 0;
+ // }
+ public void testWrongInt() {
+ loadCodeAndRun(getAboveComment());
+ checkErrorLine(5);
+ }
+
+ // int main(void) {
+ // char inputstr1[5];
+ // char inputstr2[5];
+ // int i = 5;
+ // scanf("%9s %4s", inputstr1, inputstr2);
+ // printf("%d" ,i);
+ // return 0;
+ // }
+ public void testWrongRight() {
+ loadCodeAndRun(getAboveComment());
+ checkErrorLine(5);
+ }
+
+ // int main(void) {
+ // char inputstr[5];
+ // int i = 5;
+ // scanf("%s", inputstr);
+ // printf("%d" ,i);
+ // return 0;
+ // }
+ public void testInfiniteSize() {
+ loadCodeAndRun(getAboveComment());
+ checkErrorLine(4);
+ }
+
+ // int main(void) {
+ // puts("Enter a string whose length is bellow 5:");
+ // char inputstr[5];
+ // int i = 5;
+ // scanf("%3s", inputstr);
+ // printf("%d" ,i);
+ // return 0;
+ // }
+ public void testRight() {
+ loadCodeAndRun(getAboveComment());
+ checkNoErrors();
+ }
+
+ // int main(void) {
+ // char inputstr1[5];
+ // char inputstr2[5];
+ // int i = 5;
+ // scanf("%3s %4s", inputstr1, inputstr2);
+ // printf("%d" ,i);
+ // return 0;
+ // }
+ public void testRightRight() {
+ loadCodeAndRun(getAboveComment());
+ checkNoErrors();
+ }
+
+ // int main(void) {
+ // char inputstr1[5];
+ // char inputstr2[5];
+ // int i = 5;
+ // scanf("%8s %9s", inputstr1, inputstr2);
+ // printf("%d" ,i);
+ // return 0;
+ // }
+ public void testWrongWrong() {
+ loadCodeAndRun(getAboveComment());
+ checkErrorLine(5);
+ }
+
+ // char inputstr[5];
+ // int foo(void){
+ // char inputstr[15];
+ // return 0;
+ // }
+ // int main(void) {
+ // puts("Enter a string whose length is bellow 5:");
+ // int i = 5;
+ // scanf("%10s", inputstr);
+ // printf("%d" ,i);
+ // return 0;
+ // }
+ public void testGlobalBeforeWrong() {
+ loadCodeAndRun(getAboveComment());
+ checkErrorLine(9);
+ }
+
+ // int main(void) {
+ // puts("Enter a string whose length is bellow 5:");
+ // char inputstr[15];
+ // int i = 5;
+ // scanf("%10s", inputstr);
+ // printf("%d" ,i);
+ // return 0;
+ // }
+ // int foo(void){
+ // char inputstr[5];
+ // return 0;
+ // }
+ public void testNonglobalAfterRight() {
+ loadCodeAndRun(getAboveComment());
+ checkNoErrors();
+ }
+
+ // int main(void) {
+ // char inputstr[5];
+ // int i = 5;
+ // scanf("%10s", inputstr);
+ // printf("%d" ,i);
+ // return EXIT_SUCCESS;
+ // }
+ // int foo(void){
+ // char inputstr[15];
+ // return 0;
+ // }
+ public void testNonglobalAfterWrong() {
+ loadCodeAndRun(getAboveComment());
+ checkErrorLine(4);
+ }
+
+ // int foo(void){
+ // char inputstr[5];
+ // return 0;
+ // }
+ // int main(void) {
+ // char inputstr[15];
+ // int i = 5;
+ // scanf("%10s", inputstr);
+ // printf("%d" ,i);
+ // return 0;
+ // }
+ public void testNonglobalBeforeRight() {
+ loadCodeAndRun(getAboveComment());
+ checkNoErrors();
+ }
+
+ // int foo(void){
+ // char inputstr[15];
+ // return 0;
+ // }
+ // int main(void) {
+ // char inputstr[5];
+ // int i = 5;
+ // scanf("%10s", inputstr);
+ // printf("%d" ,i);
+ // return 0;
+ // }
+ public void testNonglobalBeforeWrong() {
+ loadCodeAndRun(getAboveComment());
+ checkErrorLine(8);
+ }
+
+ // int main(void) {
+ // char inputstr1[5];
+ // int inputval;
+ // int i = 5;
+ // scanf("%i %as", inputval, inputstr1); // no error here
+ // printf("%d" ,i);
+ // return 0;
+ // }
+ public void testGaurdedRight() {
+ loadCodeAndRun(getAboveComment());
+ checkNoErrors();
+ }
+}
\ No newline at end of file
diff --git a/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/test/AutomatedIntegrationSuite.java b/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/test/AutomatedIntegrationSuite.java
index 87be458f380..251f4b97638 100644
--- a/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/test/AutomatedIntegrationSuite.java
+++ b/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/test/AutomatedIntegrationSuite.java
@@ -18,6 +18,7 @@ import org.eclipse.cdt.codan.core.internal.checkers.AssignmentInConditionChecker
import org.eclipse.cdt.codan.core.internal.checkers.AssignmentToItselfCheckerTest;
import org.eclipse.cdt.codan.core.internal.checkers.CaseBreakCheckerTest;
import org.eclipse.cdt.codan.core.internal.checkers.CatchByReferenceTest;
+import org.eclipse.cdt.codan.core.internal.checkers.FormatStringCheckerTest;
import org.eclipse.cdt.codan.core.internal.checkers.ReturnCheckerTest;
import org.eclipse.cdt.codan.core.internal.checkers.ReturnStyleCheckerTest;
import org.eclipse.cdt.codan.core.internal.checkers.StatementHasNoEffectCheckerTest;
@@ -53,6 +54,7 @@ public class AutomatedIntegrationSuite extends TestSuite {
suite.addTestSuite(ReturnStyleCheckerTest.class);
suite.addTestSuite(SuspiciousSemicolonCheckerTest.class);
suite.addTestSuite(CaseBreakCheckerTest.class);
+ suite.addTestSuite(FormatStringCheckerTest.class);
// framework
suite.addTest(CodanFastTestSuite.suite());
// quick fixes
diff --git a/codan/org.eclipse.cdt.codan.core/OSGI-INF/l10n/bundle.properties b/codan/org.eclipse.cdt.codan.core/OSGI-INF/l10n/bundle.properties
index 0f5fbdf21cc..ad252dc6852 100644
--- a/codan/org.eclipse.cdt.codan.core/OSGI-INF/l10n/bundle.properties
+++ b/codan/org.eclipse.cdt.codan.core/OSGI-INF/l10n/bundle.properties
@@ -18,3 +18,6 @@ category.name.ProgrammingErrors = Potential Programming Problems
category.name.CodeStyle = Coding Style
category.name.CompilerErrors = Syntax and Semantic Errors
extension-point.name.CodeAnalysis = Code Analysis Checkers
+
+marker.semanticError = Semantic Error
+category.name.Security = Security Vulnerabilities
\ No newline at end of file
diff --git a/codan/org.eclipse.cdt.codan.core/plugin.xml b/codan/org.eclipse.cdt.codan.core/plugin.xml
index 85e90fa7acc..dfe75711567 100644
--- a/codan/org.eclipse.cdt.codan.core/plugin.xml
+++ b/codan/org.eclipse.cdt.codan.core/plugin.xml
@@ -50,7 +50,7 @@
%15s ==> 15
+ * %128s ==> 128
+ * %s ==> infinity
+ *