Skip to content

Commit

Permalink
feat: library for program analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
tylergu committed Jun 21, 2022
1 parent 5e8d3a1 commit bc7e77a
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 20 deletions.
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
lib:
(cd k8s_util/lib && make)
(cd ssa && make)

clean:
(cd k8s_util/lib && make clean)
(cd k8s_util/lib && make clean)
(cd ssa && make)
3 changes: 2 additions & 1 deletion ssa/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.idea
.log
.log
*.so
7 changes: 7 additions & 0 deletions ssa/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.PHONY: analysis

analysis:
go build -buildmode=c-shared -o analysis.so ssa.go

clean:
rm ./analysis.so
75 changes: 75 additions & 0 deletions ssa/analysis.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* Code generated by cmd/cgo; DO NOT EDIT. */

/* package command-line-arguments */


#line 1 "cgo-builtin-export-prolog"

#include <stddef.h> /* for ptrdiff_t below */

#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H

#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
#endif

#endif

/* Start of preamble from import "C" comments. */




/* End of preamble from import "C" comments. */


/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"

#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H

typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;

/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];

#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;

#endif

/* End of boilerplate cgo prologue. */

#ifdef __cplusplus
extern "C" {
#endif

extern char* Analyze(char* projectPathPtr, char* seedTypePtr, char* seedPkgPtr);

#ifdef __cplusplus
}
#endif
15 changes: 15 additions & 0 deletions ssa/analysis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import ctypes
import json

def analyze(project_path: str, seed_type: str, seed_pkg: str) -> dict:
analysis_lib = ctypes.cdll.LoadLibrary('ssa/analysis.so')
analyze_func = analysis_lib.Analyze
analyze_func.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p]
analyze_func.restype = ctypes.c_void_p

analysis_result = analyze_func(project_path.encode("utf-8"), seed_type.encode("utf-8"), seed_pkg.encode("utf-8"))
analysis_result_bytes = ctypes.string_at(analysis_result)
return json.loads(analysis_result_bytes)

if __name__ == '__main__':
print(analyze('/home/tyler/redis-operator/cmd/redisoperator', 'RedisFailover', 'github.com/spotahome/redis-operator/api/redisfailover/v1'))
92 changes: 83 additions & 9 deletions ssa/ssa.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package main

import "C"
import (
"flag"
"fmt"
Expand All @@ -9,12 +10,80 @@ import (

"encoding/json"

"io/ioutil"

"github.com/xlab-uiuc/acto/ssa/analysis"
"github.com/xlab-uiuc/acto/ssa/util"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/ssa/ssautil"
)

//export Analyze
func Analyze(projectPathPtr *C.char, seedTypePtr *C.char, seedPkgPtr *C.char) *C.char {
projectPath := C.GoString(projectPathPtr)
seedType := C.GoString(seedTypePtr)
seedPkg := C.GoString(seedPkgPtr)

log.SetOutput(ioutil.Discard)

analysisResult := analyze(projectPath, seedType, seedPkg)
return C.CString(analysisResult)
}

func analyze(projectPath string, seedType string, seedPkgPath string) string {
cfg := packages.Config{
Mode: packages.NeedModule | packages.LoadAllSyntax,
Dir: projectPath,
}
initial, err := packages.Load(&cfg, ".")
log.Printf("Got %d initial packages\n", len(initial))
if err != nil {
log.Println(err)
}

// Create SSA packages for well-typed packages and their dependencies.
prog, _ := ssautil.AllPackages(initial, 0)

// Build SSA code for the whole program.
prog.Build()

context := analysis.Context{
Program: prog,
MainPackages: ssautil.MainPackages(prog.AllPackages()),
RootModule: initial[0].Module,
}

valueFieldSetMap, frontierSet := analysis.GetValueToFieldMappingPass(context, prog, &seedType, &seedPkgPath)
fieldSets := []util.FieldSet{}
for v, path := range valueFieldSetMap {
if _, ok := frontierSet[v]; ok {
fieldSets = append(fieldSets, *path)
}
}
mergedFieldSet := util.MergeFieldSets(fieldSets...)

taintAnalysisResult := TaintAnalysisResult{}
for _, field := range mergedFieldSet.Fields() {
taintAnalysisResult.UsedPaths = append(taintAnalysisResult.UsedPaths, field.Path)
}
taintedFieldSet := util.FieldSet{}
taintedSet := analysis.TaintAnalysisPass(context, prog, frontierSet, valueFieldSetMap)
for tainted := range taintedSet {
for _, path := range valueFieldSetMap[tainted].Fields() {
log.Printf("Path [%s] taints\n", path.Path)
taintedFieldSet.Add(&path)
}
log.Printf("value %s with path %s\n", tainted, valueFieldSetMap[tainted])
// tainted.Parent().WriteTo(log.Writer())
}
for _, field := range taintedFieldSet.Fields() {
taintAnalysisResult.TaintedPaths = append(taintAnalysisResult.TaintedPaths, field.Path)
}

marshalled, _ := json.MarshalIndent(taintAnalysisResult, "", "\t")
return string(marshalled[:])
}

func main() {
// Load, parse, and type-check the whole program.

Expand Down Expand Up @@ -94,31 +163,36 @@ func main() {
// }
log.Println("------------------------")

taintAnalysisResult := TaintAnalysisResult{}
for _, field := range mergedFieldSet.Fields() {
taintAnalysisResult.UsedPaths = append(taintAnalysisResult.UsedPaths, field.Path)
}

taintedFieldSet := util.FieldSet{}
taintedSet := analysis.TaintAnalysisPass(context, prog, frontierSet, valueFieldSetMap)
for tainted := range taintedSet {
for _, path := range valueFieldSetMap[tainted].Fields() {
log.Printf("Path [%s] taints\n", path.Path)
mergedFieldSet.Delete(&path)
taintedFieldSet.Add(&path)
}
log.Printf("value %s with path %s\n", tainted, valueFieldSetMap[tainted])
// tainted.Parent().WriteTo(log.Writer())
}

controlFlowResult := ControlFlowResult{}
for _, field := range mergedFieldSet.Fields() {
log.Printf("Path %s does not flow into k8s\n", field)
controlFlowResult.Paths = append(controlFlowResult.Paths, field.Path)
for _, field := range taintedFieldSet.Fields() {
taintAnalysisResult.TaintedPaths = append(taintAnalysisResult.TaintedPaths, field.Path)
}

controlFlowResultFile, err := os.Create("controlFlowResult.json")
if err != nil {
log.Fatalf("Failed to create mapping.txt to write mapping: %v\n", err)
}
defer controlFlowResultFile.Close()

marshalled, _ := json.MarshalIndent(controlFlowResult, "", "\t")
marshalled, _ := json.MarshalIndent(taintAnalysisResult, "", "\t")
controlFlowResultFile.Write(marshalled)
}

type ControlFlowResult struct {
Paths [][]string `json:"paths"`
type TaintAnalysisResult struct {
UsedPaths [][]string `json:"usedPaths"`
TaintedPaths [][]string `json:"taintedPaths"`
}
13 changes: 4 additions & 9 deletions ssa/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"
"go/types"
"log"
"os"
"strconv"
"strings"

Expand Down Expand Up @@ -122,21 +121,17 @@ func FindSeedType(prog *ssa.Program, seedStr *string, pkgPath *string) *ssa.Type

func FindSeedValues(prog *ssa.Program, seedType *string, pkgPath *string) []ssa.Value {
seedVariables := []ssa.Value{}
seedOutFile, err := os.Create("seed.txt")
if err != nil {
log.Fatalf("Failed to create file %s\n", err)
}

seed := FindSeedType(prog, seedType, pkgPath)
if seed != nil {
log.Println(seed.String())
if seedStruct, ok := seed.Type().Underlying().(*types.Struct); ok {
for i := 0; i < seedStruct.NumFields(); i++ {
field := seedStruct.Field(i)
seedOutFile.WriteString(fmt.Sprintf("%s - %s\n", field.Name(), GetFieldNameFromJsonTag(seedStruct.Tag(i))))
log.Printf("%s\n", fmt.Sprintf("%s - %s\n", field.Name(), GetFieldNameFromJsonTag(seedStruct.Tag(i))))
}
} else {
seedOutFile.WriteString(fmt.Sprintf("%T", seed.Type().Underlying()))
log.Printf("%s\n", fmt.Sprintf("%T", seed.Type().Underlying()))
}
}

Expand All @@ -152,9 +147,9 @@ func FindSeedValues(prog *ssa.Program, seedType *string, pkgPath *string) []ssa.
}

for _, seedVar := range seedVariables {
seedOutFile.WriteString(fmt.Sprintf("Value %s is seed\n", seedVar.String()))
log.Printf("%s\n", fmt.Sprintf("Value %s is seed\n", seedVar.String()))
}
seedOutFile.WriteString(fmt.Sprintf("%d\n", len(seedVariables)))
log.Printf("%s\n", fmt.Sprintf("%d\n", len(seedVariables)))

return seedVariables
}
Expand Down

0 comments on commit bc7e77a

Please sign in to comment.