Skip to content

Commit

Permalink
Propagate through calls more selectively to avoid false positives (#292)
Browse files Browse the repository at this point in the history
Specifically, avoid propagating in the absence of knowledge about a
function's behavior. As a mitigation, summaries for a large number
of standard library functions are added and used.
  • Loading branch information
mlevesquedion authored Apr 6, 2021
1 parent 920c414 commit 0808815
Show file tree
Hide file tree
Showing 17 changed files with 1,264 additions and 314 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,6 @@ func (s Source) CopyPointer() (*Source, error) {
return &s, nil
}

func (s Source) Propagate(str string) string {
return str
}

type TaggedSource struct {
Data string `levee:"source"`
ID int
Expand All @@ -62,7 +58,3 @@ func (i Innocuous) GetID() int {
func (i Innocuous) GetData() string {
return i.Data
}

type SourceManipulator interface {
Propagate(string) string
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,56 +15,31 @@
package callorder

import (
"fmt"
"io"
"levee_analysistest/example/core"
)

func TestTaintedColocatedArgumentDoesNotReachSinkThatPrecedesColocation() {
s := core.Source{}
i := newInnocuous()
if err := fail(i); err != nil {
core.Sink(err)
func TestTaintedColocatedArgumentDoesNotReachSinkThatPrecedesColocation(w io.Writer, src core.Source) {
if true {
core.Sink(w)
}
taintColocated(s, i)
fmt.Fprint(w, src)
}

func TestTaintedColocatedArgumentReachesSinkThatFollowsColocation() {
s := core.Source{}
i := newInnocuous()
taintColocated(s, i)
if err := fail(i); err != nil {
core.Sink(err) // want "a source has reached a sink"
func TestTaintedColocatedArgumentReachesSinkThatFollowsColocation(w io.Writer, src core.Source) {
if _, err := fmt.Fprint(w, src); err != nil {
core.Sink(w) // want "a source has reached a sink"
}
}

func TestAvoidingIncorrectPropagationFromColocationDoesNotPreventCorrectReport() {
source := newSource()

cp, err := copy(source)
func TestAvoidingIncorrectPropagationFromColocationDoesNotPreventCorrectReport(w io.Writer, src core.Source) {
_, err := fmt.Fprint(w, src)
if err != nil {
core.Sink(err) // want "a source has reached a sink"
core.Sink(w) // want "a source has reached a sink"
}

if true {
innoc := newInnocuous()
taintColocated(cp, innoc)
fmt.Fprint(w, src)
}
}

func fail(x interface{}) error {
return nil
}

func taintColocated(a, b interface{}) {
}

func newInnocuous() *core.Innocuous {
return &core.Innocuous{}
}

func newSource() *core.Source {
return &core.Source{}
}

func copy(a interface{}) (interface{}, error) {
return nil, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,120 +16,10 @@ package colocation

import (
"encoding/json"
"reflect"

"levee_analysistest/example/core"
)

func colocateString(core.Source, string) {}

func TestBasicTypeIsNotTainted(s core.Source, str string) {
colocateString(s, str)
core.Sink(str)
}

func colocateStringPointer(core.Source, *string) {}

func TestBasicPointerTypeIsTainted(s core.Source, strptr *string) {
colocateStringPointer(s, strptr)
core.Sink(strptr) // want "a source has reached a sink"
}

func TestPointerToBasicTypeIsTainted(s core.Source, str string) {
// This test is failing because &x introduces an Alloc,
// and we don't traverse through non-array Allocs
colocateStringPointer(s, &str)
core.Sink(str) // TODO(212) want "a source has reached a sink"
}

func colocateInnoc(core.Source, core.Innocuous) {}

func TestNamedStructTypeIsNotTainted(s core.Source, i core.Innocuous) {
colocateInnoc(s, i)
core.Sink(i)
}

func colocateInnocPtr(core.Source, *core.Innocuous) {}

func TestNamedStructPointerIsTainted(s core.Source, i *core.Innocuous) {
colocateInnocPtr(s, i)
core.Sink(i) // want "a source has reached a sink"
}

func TestPointerToNamedStructIsTainted(s core.Source, i core.Innocuous) {
// This test is failing because &x introduces an Alloc,
// and we don't traverse through non-array Allocs
colocateInnocPtr(s, &i)
core.Sink(i) // TODO(212) want "a source has reached a sink"
}

type PointerHolder struct{ ptr *core.Source }

func colocatePointerHolder(core.Source, PointerHolder) {}

func TestNamedStructPointerHolderIsTainted(s core.Source, ph PointerHolder) {
// This test is failing because ph is created by an Alloc,
// and we don't traverse through non-array Allocs
colocatePointerHolder(s, ph)
core.Sink(ph) // TODO(212) want "a source has reached a sink"
}

func colocateArrOfValues(core.Source, [1]string) {}

func TestArrOfValuesIsNotTainted(s core.Source, arr [1]string) {
colocateArrOfValues(s, arr)
core.Sink(arr)
}

func colocateArrOfPointers(core.Source, [1]*string) {}

func TestArrOfPointersIsTainted(s core.Source, arr [1]*string) {
colocateArrOfPointers(s, arr)
core.Sink(arr) // want "a source has reached a sink"
}

func colocateReferenceCollections(core.Source, map[string]string, chan string, []string) {}

func TestReferenceCollectionsAreTainted(s core.Source) {
m := make(map[string]string)
c := make(chan string)
sl := make([]string, 0)
colocateReferenceCollections(s, m, c, sl)
core.Sink(m) // want "a source has reached a sink"
core.Sink(c) // want "a source has reached a sink"
core.Sink(sl) // want "a source has reached a sink"
}

func colocateReflectValue(core.Source, reflect.Value) {}

func TestReflectValuesAreTainted(s core.Source, r reflect.Value) {
// This test is failing because r is created by an Alloc,
// and we don't traverse through non-array Allocs
colocateReflectValue(s, r)
core.Sink(r) // TODO(212) want "a source has reached a sink"
}

func colocateSingleInterface(s core.Source, e interface{}) {}
func colocateVariadicInterface(s core.Source, taintees ...interface{}) {}

func TestTaintedInterface(s core.Source, i interface{}) {
colocateSingleInterface(s, i)
core.Sink(i) // want "a source has reached a sink"

}

func TestTaintedThroughInterface(s core.Source, str string, i core.Innocuous) {
colocateVariadicInterface(s, str, i)
core.Sink(str)
core.Sink(i)
}

func TestPointerTaintedThroughInterface(s core.Source, str string, i core.Innocuous) {
colocateVariadicInterface(s, &str, &i)
core.Sink(str) // TODO(212) want "a source has reached a sink"
core.Sink(i) // TODO(212) want "a source has reached a sink"
}

// CVE-2020-8564
func TestTaintIsPropagatedToDataBeingUnmarshalled(contents []byte) (src core.Source, err error) {
if err = json.Unmarshal(contents, &src); err != nil {
Expand All @@ -141,18 +31,3 @@ func TestTaintIsPropagatedToDataBeingUnmarshalled(contents []byte) (src core.Sou
core.Sink(contents) // want "a source has reached a sink"
return
}

func colocateFunc(core.Source, func()) {}

func TestTaintIsNotPropagatedToFunction(s core.Source) {
f := func() {}
// f is an *ssa.Function with type *types.Signature
colocateFunc(s, f)
core.Sink(f)
}

func TestTaintIsNotPropagatedToFunctionParameter(s core.Source, f func()) {
// f is an *ssa.Parameter with type *types.Signature
colocateFunc(s, f)
core.Sink(f)
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ func CreateSourceFlipped() (error, core.Source) {
return nil, core.Source{}
}

func TakeSource(s core.Source) (string, int, interface{}) {
return "", 0, nil
}

func TestOnlySourceExtractIsTaintedFromCall() {
s, ok := TryUpdateSource(core.Source{})
core.Sink(s) // want "a source has reached a sink"
Expand Down Expand Up @@ -77,13 +73,6 @@ func TestOnlySourceExtractIsTaintedInstructionOrderFlipped() {
core.Sink(s) // want "a source has reached a sink"
}

func TestExtractsFromCallWithSourceArgAreTainted(s core.Source) {
str, i, e := TakeSource(s)
core.Sink(str) // want "a source has reached a sink"
core.Sink(i)
core.Sink(e) // want "a source has reached a sink"
}

func NewSource() (*core.Source, error) {
return &core.Source{}, nil
}
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,13 @@
package propagation

import (
"fmt"

"errors"
"levee_analysistest/example/core"
"os"
)

func Identity(arg interface{}) interface{} {
return arg
}

func TestIdentityPropagator(s core.Source) {
i := Identity(s)
core.Sink(i) // want "a source has reached a sink"
core.Sink(Identity(s)) // want "a source has reached a sink"
}

func ToString(arg interface{}) string {
return fmt.Sprintf("%v", arg)
}

func TestToStringPropagator(s core.Source) {
v := ToString(s)
core.Sink(v) // want "a source has reached a sink"
}

func TestPropagationViaSourceMethod(s core.Source) {
tainted := s.Propagate(s.Data)
core.Sink(tainted) // want "a source has reached a sink"
}

func TestPropagationViaFunctionReturningBool(s *core.Source, i *core.Innocuous) {
if ok := TryCopy(i, s); !ok {
core.Sinkf("couldn't copy to: %v", i) // want "a source has reached a sink"
func TestPropagationViaFunctionReturningBool(s core.Source, err *os.PathError) {
if ok := errors.As(errors.New(s.Data), err); !ok {
core.Sinkf("not a PathError: %v", err) // want "a source has reached a sink"
}
}

func TryCopy(dst interface{}, src interface{}) bool {
return true
}
Loading

0 comments on commit 0808815

Please sign in to comment.