diff --git a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluator.kt b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluator.kt index 04ddcdd46c..4c3c2a14c7 100644 --- a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluator.kt +++ b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluator.kt @@ -163,6 +163,11 @@ open class ValueEvaluator( "/=" -> handleDiv(lhsValue, rhsValue, expr) "*", "*=" -> handleTimes(lhsValue, rhsValue, expr) + "<<" -> handleShiftLeft(lhsValue, rhsValue, expr) + ">>" -> handleShiftRight(lhsValue, rhsValue, expr) + "&" -> handleBitwiseAnd(lhsValue, rhsValue, expr) + "|" -> handleBitwiseOr(lhsValue, rhsValue, expr) + "^" -> handleBitwiseXor(lhsValue, rhsValue, expr) ">" -> handleGreater(lhsValue, rhsValue, expr) ">=" -> handleGEq(lhsValue, rhsValue, expr) "<" -> handleLess(lhsValue, rhsValue, expr) @@ -202,6 +207,51 @@ open class ValueEvaluator( } } + private fun handleShiftLeft(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? { + return when { + // right side must always be an int + lhsValue is Int && rhsValue is Int -> lhsValue shl rhsValue + lhsValue is Long && rhsValue is Int -> lhsValue shl rhsValue + else -> cannotEvaluate(expr, this) + } + } + + private fun handleShiftRight(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? { + return when { + // right side must always be an int + lhsValue is Int && rhsValue is Int -> lhsValue shr rhsValue + lhsValue is Long && rhsValue is Int -> lhsValue shr rhsValue + else -> cannotEvaluate(expr, this) + } + } + + private fun handleBitwiseAnd(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? { + return when { + // left and right must be equal and only long and int are supported + lhsValue is Int && rhsValue is Int -> lhsValue and rhsValue + lhsValue is Long && rhsValue is Long -> lhsValue and rhsValue + else -> cannotEvaluate(expr, this) + } + } + + private fun handleBitwiseOr(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? { + return when { + // left and right must be equal and only long and int are supported + lhsValue is Int && rhsValue is Int -> lhsValue or rhsValue + lhsValue is Long && rhsValue is Long -> lhsValue or rhsValue + else -> cannotEvaluate(expr, this) + } + } + + private fun handleBitwiseXor(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? { + return when { + // left and right must be equal and only long and int are supported + lhsValue is Int && rhsValue is Int -> lhsValue xor rhsValue + lhsValue is Long && rhsValue is Long -> lhsValue xor rhsValue + else -> cannotEvaluate(expr, this) + } + } + private fun handleGreater(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? { return if (lhsValue is Number && rhsValue is Number) { lhsValue.compareTo(rhsValue) > 0 diff --git a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluatorTest.kt b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluatorTest.kt index 91d8deb9b0..500144e2ab 100644 --- a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluatorTest.kt +++ b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluatorTest.kt @@ -639,6 +639,106 @@ class ValueEvaluatorTest { } } + @Test + fun testHandleShiftLeft() { + with(TestHandler(TestLanguageFrontend())) { + val binOp = newBinaryOperator("<<") + // Int.plus + binOp.lhs = newLiteral(3, primitiveType("int")) + binOp.rhs = newLiteral(2, primitiveType("int")) + assertEquals(12, ValueEvaluator().evaluate(binOp)) + + // Long.plus + binOp.lhs = newLiteral(3L, primitiveType("long")) + binOp.rhs = newLiteral(2, primitiveType("int")) + assertEquals(12L, ValueEvaluator().evaluate(binOp)) + + binOp.lhs = newLiteral("Hello", primitiveType("string")) + binOp.rhs = newLiteral(" world", primitiveType("string")) + assertEquals("{<<}", ValueEvaluator().evaluate(binOp)) + } + } + + @Test + fun testHandleShiftRight() { + with(TestHandler(TestLanguageFrontend())) { + val binOp = newBinaryOperator(">>") + // Int.plus + binOp.lhs = newLiteral(3, primitiveType("int")) + binOp.rhs = newLiteral(2, primitiveType("int")) + assertEquals(0, ValueEvaluator().evaluate(binOp)) + + // Long.plus + binOp.lhs = newLiteral(3L, primitiveType("long")) + binOp.rhs = newLiteral(2, primitiveType("int")) + assertEquals(0L, ValueEvaluator().evaluate(binOp)) + + binOp.lhs = newLiteral("Hello", primitiveType("string")) + binOp.rhs = newLiteral(" world", primitiveType("string")) + assertEquals("{>>}", ValueEvaluator().evaluate(binOp)) + } + } + + @Test + fun testHandleBitwiseAnd() { + with(TestHandler(TestLanguageFrontend())) { + val binOp = newBinaryOperator("&") + // Int.plus + binOp.lhs = newLiteral(3, primitiveType("int")) + binOp.rhs = newLiteral(2, primitiveType("int")) + assertEquals(2, ValueEvaluator().evaluate(binOp)) + + // Long.plus + binOp.lhs = newLiteral(3L, primitiveType("long")) + binOp.rhs = newLiteral(2L, primitiveType("long")) + assertEquals(2L, ValueEvaluator().evaluate(binOp)) + + binOp.lhs = newLiteral("Hello", primitiveType("string")) + binOp.rhs = newLiteral(" world", primitiveType("string")) + assertEquals("{&}", ValueEvaluator().evaluate(binOp)) + } + } + + @Test + fun testHandleBitwiseOr() { + with(TestHandler(TestLanguageFrontend())) { + val binOp = newBinaryOperator("|") + // Int.plus + binOp.lhs = newLiteral(3, primitiveType("int")) + binOp.rhs = newLiteral(2, primitiveType("int")) + assertEquals(3, ValueEvaluator().evaluate(binOp)) + + // Long.plus + binOp.lhs = newLiteral(3L, primitiveType("long")) + binOp.rhs = newLiteral(2L, primitiveType("long")) + assertEquals(3L, ValueEvaluator().evaluate(binOp)) + + binOp.lhs = newLiteral("Hello", primitiveType("string")) + binOp.rhs = newLiteral(" world", primitiveType("string")) + assertEquals("{|}", ValueEvaluator().evaluate(binOp)) + } + } + + @Test + fun testHandleBitwiseXor() { + with(TestHandler(TestLanguageFrontend())) { + val binOp = newBinaryOperator("^") + // Int.plus + binOp.lhs = newLiteral(3, primitiveType("int")) + binOp.rhs = newLiteral(2, primitiveType("int")) + assertEquals(1, ValueEvaluator().evaluate(binOp)) + + // Long.plus + binOp.lhs = newLiteral(3L, primitiveType("long")) + binOp.rhs = newLiteral(2L, primitiveType("long")) + assertEquals(1L, ValueEvaluator().evaluate(binOp)) + + binOp.lhs = newLiteral("Hello", primitiveType("string")) + binOp.rhs = newLiteral(" world", primitiveType("string")) + assertEquals("{^}", ValueEvaluator().evaluate(binOp)) + } + } + @Test fun testHandleUnary() { with(TestHandler(TestLanguageFrontend())) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt index 695b7332bc..75aadb2644 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt @@ -164,6 +164,9 @@ abstract class Language> : Node() { "-", "*", "/" -> arithmeticOpTypePropagation(operation.lhs.type, operation.rhs.type) + "&", + "|", + "^", "<<", ">>" -> if (operation.lhs.type.isPrimitive && operation.rhs.type.isPrimitive) { diff --git a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/DeclarationTest.kt b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/DeclarationTest.kt index 62a1cb78db..6a3be0a564 100644 --- a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/DeclarationTest.kt +++ b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/DeclarationTest.kt @@ -303,10 +303,16 @@ class DeclarationTest { with(tu) { val values = mapOf( + "zeroShift" to Pair(0, objectType("int")), + "zeroAnd" to Pair(0, objectType("int")), "one" to Pair(1, objectType("p.custom")), "oneAsWell" to Pair(1, objectType("p.custom")), + "oneShift" to Pair(1, primitiveType("int")), "two" to Pair(2, primitiveType("int")), + "twoShift" to Pair(2, primitiveType("int")), "three" to Pair(3, primitiveType("int")), + "threeOr" to Pair(3, primitiveType("int")), + "threeXor" to Pair(3, primitiveType("int")), "four" to Pair(4, primitiveType("int")), "tenAsWell" to Pair(10, primitiveType("int")), "five" to Pair(5, primitiveType("int")), diff --git a/cpg-language-go/src/test/resources/golang/const.go b/cpg-language-go/src/test/resources/golang/const.go index bd87af2206..3338dbfa3c 100644 --- a/cpg-language-go/src/test/resources/golang/const.go +++ b/cpg-language-go/src/test/resources/golang/const.go @@ -24,3 +24,12 @@ const ( fiveAsWell = 5 + iota*100 onehundredandfive ) + +const ( + oneShift = 1 << iota + twoShift + zeroShift = 1 >> iota + zeroAnd = oneShift & twoShift + threeOr = oneShift | twoShift + threeXor = oneShift ^ twoShift +)