From 329bbdaa6a84d10a79e2a90b526e6dc1421a29a7 Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Fri, 13 Oct 2023 20:49:08 +0200 Subject: [PATCH] =?UTF-8?q?Support=20for=20`<<`,=20`>>`=20and=20`|`=C2=A0i?= =?UTF-8?q?n=20`ValueEvaluator`=20and=20`BinaryOperation`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aisec/cpg/analysis/ValueEvaluator.kt | 50 +++++++++ .../aisec/cpg/analysis/ValueEvaluatorTest.kt | 100 ++++++++++++++++++ .../aisec/cpg/frontends/Language.kt | 1 + .../cpg/frontends/golang/DeclarationTest.kt | 4 + .../src/test/resources/golang/const.go | 7 ++ 5 files changed, 162 insertions(+) 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 04ddcdd46c7..4c3c2a14c72 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 91d8deb9b08..500144e2ab6 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 695b7332bc3..70aee4462c4 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,7 @@ 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 62a1cb78db6..c65e3b52672 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,14 @@ class DeclarationTest { with(tu) { val values = mapOf( + "zeroShift" 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")), + "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 bd87af22061..b4426c48ece 100644 --- a/cpg-language-go/src/test/resources/golang/const.go +++ b/cpg-language-go/src/test/resources/golang/const.go @@ -24,3 +24,10 @@ const ( fiveAsWell = 5 + iota*100 onehundredandfive ) + +const ( + oneShift = 1 << iota + twoShift + zeroShift = 1 >> iota + threeXor = oneShift | twoShift +)