Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.opentaint.go.config

import java.net.URI
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.copyToRecursively
import kotlin.io.path.createTempDirectory
import kotlin.streams.asSequence

object GoDefaultModelLoader {
private const val MODEL_ROOT = "/model/go/dataflow"

private val unpackedModelPaths: List<Path> by lazy {
loadModelPaths()
}

fun modelPaths(): List<Path> = unpackedModelPaths

private fun loadModelPaths(): List<Path> {
val resource = javaClass.getResource(MODEL_ROOT) ?: return emptyList()
return when (resource.protocol) {
"file" -> findModelModules(Path.of(resource.toURI()))
"jar" -> unpackJarModels(resource.toURI())
else -> error("Unsupported bundled Go model resource protocol: ${resource.protocol}")
}
}

@OptIn(ExperimentalPathApi::class)
private fun unpackJarModels(resource: URI): List<Path> {
val unpacked = createTempDirectory("opentaint-go-models")
FileSystems.newFileSystem(resource, emptyMap<String, Any>()).use { fs ->
fs.getPath(MODEL_ROOT).copyToRecursively(
unpacked,
followLinks = false,
overwrite = false,
)
}
return findModelModules(unpacked)
}

private fun findModelModules(root: Path): List<Path> = Files.list(root).use { paths ->
paths.asSequence()
.filter { Files.isDirectory(it) && Files.isRegularFile(it.resolve("go.mod")) }
.sortedBy { it.fileName.toString() }
.toList()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,12 @@ class GoTaintConfiguration : GoTaintRulesProvider {
patternRules: List<R>,
ruleNameMatcher: R.() -> GoNameMatcher,
): List<R> {
val direct = simpleByName[name].orEmpty()
val genericOriginName = name.substringBefore('[', name)
val direct = if (genericOriginName == name) {
simpleByName[name].orEmpty()
} else {
simpleByName[name].orEmpty() + simpleByName[genericOriginName].orEmpty()
}
val patternMatches = patternRules.filter { it.ruleNameMatcher().matches(name) }
if (patternMatches.isEmpty()) return direct
if (direct.isEmpty()) return patternMatches
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.opentaint.dataflow.go.rules

import org.opentaint.dataflow.configuration.go.serialized.GoNameMatcher
import org.opentaint.dataflow.configuration.go.serialized.GoSerializedPassAction
import org.opentaint.dataflow.configuration.go.serialized.GoSerializedRule
import org.opentaint.dataflow.configuration.go.serialized.GoSerializedTaintConfig
import org.opentaint.dataflow.configuration.jvm.serialized.PositionBase.Argument
import org.opentaint.dataflow.configuration.jvm.serialized.PositionBase.Result
import org.opentaint.dataflow.configuration.jvm.serialized.PositionBaseWithModifiers.BaseOnly
import org.opentaint.dataflow.go.GoFunctionSignature
import org.opentaint.ir.go.type.GoIRBasicType
import org.opentaint.ir.go.type.GoIRBasicTypeKind.STRING
import org.opentaint.ir.go.type.GoIRPointerType
import kotlin.test.Test
import kotlin.test.assertEquals

class GoTaintConfigurationTest {
@Test
fun `simple rule name matches a generic function instance`() {
val stringType = GoIRBasicType(STRING)
val rule = GoSerializedRule.PassThrough(
pkg = GoNameMatcher.Simple("github.com/google/go-github/v89/github"),
function = GoNameMatcher.Simple("Ptr"),
copy = listOf(
GoSerializedPassAction(
from = BaseOnly(Argument(0)),
to = BaseOnly(Result),
),
),
)
val configuration = GoTaintConfiguration().also {
it.loadConfig(GoSerializedTaintConfig(passThrough = listOf(rule)))
}
val signature = GoFunctionSignature(
name = "github.com/google/go-github/v89/github.Ptr[string]",
receiverType = null,
paramTypes = listOf(stringType),
resultType = GoIRPointerType(stringType),
pkgName = "github.com/google/go-github/v89/github",
)

assertEquals(1, configuration.passThroughForFunction(signature).size)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ interface GoIRFunction: CommonMethod {

// Generics
val typeParams: List<GoIRTypeParamDecl>
val originFullName: String? get() = null
val typeArgs: List<GoIRType> get() = emptyList()

override val parameters: List<CommonMethodParameter> get() =
if (freeVars.isEmpty()) params
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.opentaint.ir.go.api

import org.opentaint.ir.go.type.NamedTypeRef

/** Symbol resolution in one program. References cache only their own lookup result. */
interface GoIRLookup {
fun function(reference: GoIrFunctionReference): GoIRFunction?
fun namedType(reference: NamedTypeRef): GoIRNamedType?
fun global(reference: GoIRGlobalReference): GoIRGlobal?
}

class GoIRProgramLookup(private val program: GoIRProgram) : GoIRLookup {
override fun function(reference: GoIrFunctionReference): GoIRFunction? =
program.findPackage(reference.pkg)?.functions?.firstOrNull {
it.name == reference.name && it.signature == reference.signature
}
override fun namedType(reference: NamedTypeRef): GoIRNamedType? =
program.findPackage(reference.refTypePkg)?.findNamedType(reference.refTypeName)
override fun global(reference: GoIRGlobalReference): GoIRGlobal? =
program.findPackage(reference.pkg)?.findGlobal(reference.name)
}

data class GoIRGlobalReference(val pkg: String, val name: String) {
lateinit var program: GoIRProgram
val global: GoIRGlobal by lazy {
program.lookup.global(this) ?: error("Could not find global $pkg.$name")
}
}

fun GoIRGlobal.reference(): GoIRGlobalReference = GoIRGlobalReference(pkg.importPath, name)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.opentaint.ir.go.api

/** A function supplied by a model, including helpers with no original declaration. */
interface GoIRModelFunction : GoIRFunction

/** A model implementation of an existing program function. */
interface GoIRApproximatedFunction : GoIRModelFunction {
val original: GoIRFunction
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import org.opentaint.ir.go.type.GoIRAnonymousInterfaceType
* Top-level entity: a loaded Go program with all its packages.
*/
interface GoIRProgram {
val lookup: GoIRLookup get() = GoIRProgramLookup(this)

val packages: Map<String, GoIRPackage> // keyed by import path
val anonymousInterfaces: Map<Int, GoIRAnonymousInterfaceType>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.opentaint.ir.go.api

/** A package lookup interceptor. Unhandled requests are passed to the next resolver. */
interface GoIRTransformer {
fun resolvePackage(importPath: String, next: () -> GoIRPackage?): GoIRPackage? = next()
val additionalPackages: List<GoIRPackage> get() = emptyList()
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,11 @@ data class GoIrFunctionReference(

lateinit var program: GoIRProgram

val function: GoIRFunction
get() {
val p = program.findPackage(pkg)
?: error("Could not find package $pkg for $fullName")
// Match on name + signature because methods on different named
// types legitimately share short names (e.g. `Dog.Legs` and
// `Snake.Legs`); only the signature's receiver disambiguates them.
return p.functions.firstOrNull { it.name == name && it.signature == signature }
?: error("Could not find function $fullName with signature $signature")
}
val function: GoIRFunction by lazy {
program.lookup.function(this)
?: error("Could not find function $fullName with signature $signature")
}
}

fun GoIRFunction.reference(): GoIrFunctionReference =
GoIrFunctionReference(pkg?.importPath.orEmpty(), name, signature)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.opentaint.ir.go.cfg

import org.opentaint.ir.go.api.GoIRFunction
import org.opentaint.ir.go.api.GoIrFunctionReference
import org.opentaint.ir.go.api.GoIRPosition
import org.opentaint.ir.go.type.GoIRCallMode
import org.opentaint.ir.go.type.GoIRChanDirection
Expand All @@ -11,8 +11,9 @@ sealed interface GoIRCallTarget {
val type: GoIRType
val displayName: String

data class Function(val function: GoIRFunction) : GoIRCallTarget {
override val type: GoIRType get() = function.signature
data class Function(val lookup: GoIrFunctionReference) : GoIRCallTarget {
val function get() = lookup.function
override val type: GoIRType get() = lookup.signature
override val displayName: String get() = function.fullName
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,10 @@ data class GoIRTypeAssertExpr(

data class GoIRMakeClosureExpr(
override var type: GoIRType,
val fn: GoIRFunction,
val lookup: org.opentaint.ir.go.api.GoIrFunctionReference,
val bindings: List<GoIRValue>,
) : GoIRExpr {
val fn get() = lookup.function
override val operands: List<GoIRValue> get() = bindings
override fun <T> accept(visitor: GoIRExprVisitor<T>): T = visitor.visitMakeClosure(this)
override fun toString(): String = "makeclosure ${fn.name}[${bindings.joinToString(", ")}]"
Expand Down Expand Up @@ -310,17 +311,19 @@ data class GoIRExtractExpr(

data class GoIRGlobalValueExpr(
override var type: GoIRType,
val global: GoIRGlobal,
val lookup: org.opentaint.ir.go.api.GoIRGlobalReference,
) : GoIRExpr {
val global get() = lookup.global
override val operands: List<GoIRValue> get() = emptyList()
override fun <T> accept(visitor: GoIRExprVisitor<T>): T = visitor.visitGlobalValue(this)
override fun toString(): String = "global ${global.fullName}"
}

data class GoIRFunctionValueExpr(
override var type: GoIRType,
val function: GoIRFunction,
val lookup: org.opentaint.ir.go.api.GoIrFunctionReference,
) : GoIRExpr {
val function get() = lookup.function
override val operands: List<GoIRValue> get() = emptyList()
override fun <T> accept(visitor: GoIRExprVisitor<T>): T = visitor.visitFunctionValue(this)
override fun toString(): String = "func ${function.fullName}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,10 @@ data class GoIRIndexStore(

data class GoIRGlobalStore(
override val location: GoInstLocation,
val global: GoIRGlobal,
val lookup: org.opentaint.ir.go.api.GoIRGlobalReference,
val value: GoIRValue,
) : GoIRInst {
val global get() = lookup.global
override val operands: List<GoIRValue> get() = listOf(value)
override fun <T> accept(visitor: GoIRInstVisitor<T>): T = visitor.visitGlobalStore(this)
override fun toString(): String = "global ${global.fullName} = $value"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ data class NamedTypeRef(
val fullRefName: String get() = "$refTypePkg.$refTypeName"

val namedType: GoIRNamedType
get() = program.findPackage(refTypePkg)?.findNamedType(refTypeName)
get() = program.lookup.namedType(this)
?: error("Can't find namedType for $fullRefName")

override fun toString(): String = "ref($fullRefName)"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.opentaint.ir.go.client

import org.opentaint.ir.go.api.*
import org.opentaint.ir.go.type.*

/** Symbol resolution policy applied while a body is decoded, independent of wire decoding. */
open class GoIRBodyContext {
open fun type(type: GoIRType): GoIRType = type
open fun function(reference: GoIrFunctionReference): GoIrFunctionReference = reference
open fun global(reference: GoIRGlobalReference): GoIRGlobalReference = reference
open fun fieldIndex(type: GoIRType, index: Int, name: String): Int = index
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,15 @@ class GoIRClient : AutoCloseable {
.addAllProjectModulePaths(config.projectModules)
.setInstantiateGenerics(config.instantiateGenerics)
.setSanityCheck(config.sanityCheck)
.addAllModelDirs(config.modelDirs.map { it.toAbsolutePath().toString() })
.build()

val totalStart = System.nanoTime()
val deserializer = GoIRDeserializer()
val deserializeStart = System.nanoTime()
val program = try {
val responses = stub.buildProgram(request)
deserializer.deserialize(responses)
GoIRModelLoader.apply(deserializer.deserialize(responses))
} catch (e: StatusRuntimeException) {
// gRPC only says e.g. "UNAVAILABLE: Network closed" when the server
// process dies mid-stream. Attach the server's exit state and stderr
Expand Down
Loading
Loading