├── .circleci └── config.yml ├── .gitignore ├── LICENSE ├── README.md ├── cmd └── chronos │ └── main.go ├── domain ├── BlockState.go ├── Context.go ├── DeferFunction.go ├── FunctionState.go ├── GuardedAccess.go ├── Lockset.go └── VectorClock.go ├── go.mod ├── go.sum ├── output └── warnings.go ├── pointerAnalysis └── PointerAnalysis.go ├── ssaPureUtils ├── General.go ├── Locks.go └── Structs.go ├── ssaUtils ├── CFG.go ├── Functions.go ├── Functions_test.go ├── Locks.go ├── Packages.go ├── PreProcess.go ├── testdata │ ├── Functions │ │ ├── Defer │ │ │ ├── DeferredLockAndUnlockIfBranch │ │ │ │ └── prog1.go │ │ │ ├── NestedDeferWithLockAndUnlock │ │ │ │ └── prog1.go │ │ │ └── NestedDeferWithLockAndUnlockAndGoroutine │ │ │ │ └── prog1.go │ │ ├── ForLoops │ │ │ ├── ForLoopLockInsideLoop │ │ │ │ └── prog1.go │ │ │ ├── ForLoopLockOutsideLoop │ │ │ │ └── prog1.go │ │ │ ├── NestedForLoopWithRace │ │ │ │ └── prog1.go │ │ │ ├── WhileLoop │ │ │ │ └── prog1.go │ │ │ └── WhileLoopWithoutHeader │ │ │ │ └── prog1.go │ │ ├── General │ │ │ ├── DataRaceGoto │ │ │ │ └── prog1.go │ │ │ ├── DataRaceMap │ │ │ │ └── prog1.go │ │ │ ├── DataRaceNestedSameFunction │ │ │ │ └── prog1.go │ │ │ ├── DataRaceProperty │ │ │ │ └── prog1.go │ │ │ ├── DataRaceRecursion │ │ │ │ └── prog1.go │ │ │ ├── DataRaceShadowedErr │ │ │ │ └── prog1.go │ │ │ ├── DataRaceWithOnlyAlloc │ │ │ │ └── prog1.go │ │ │ ├── DataRaceWithSameFunction │ │ │ │ └── prog1.go │ │ │ ├── ElseIf │ │ │ │ └── prog1.go │ │ │ ├── NestedFunctions │ │ │ │ └── prog1.go │ │ │ ├── RecursionWithGoroutine │ │ │ │ └── prog1.go │ │ │ ├── RecursionWithRecover │ │ │ │ └── prog1.go │ │ │ ├── Simple │ │ │ │ └── prog1.go │ │ │ └── StructMethod │ │ │ │ └── prog1.go │ │ ├── Interfaces │ │ │ ├── DataRaceIceCreamMaker │ │ │ │ └── prog1.go │ │ │ ├── InterfaceWithLock │ │ │ │ └── prog1.go │ │ │ └── NestedInterface │ │ │ │ └── prog1.go │ │ ├── LocksAndUnlocks │ │ │ ├── Lock │ │ │ │ └── prog1.go │ │ │ ├── LockAndUnlock │ │ │ │ └── prog1.go │ │ │ ├── LockAndUnlockIfBranch │ │ │ │ └── prog1.go │ │ │ ├── LockInBothBranches │ │ │ │ └── prog1.go │ │ │ ├── LockInsideGoroutine │ │ │ │ └── prog1.go │ │ │ ├── MultipleLocksNoRace │ │ │ │ └── prog1.go │ │ │ ├── MultipleLocksRace │ │ │ │ └── prog1.go │ │ │ ├── NestedConditionWithLockInAllBranches │ │ │ │ └── prog1.go │ │ │ └── NestedLockInStruct │ │ │ │ └── prog1.go │ │ └── PointerAnalysis │ │ │ └── DataRaceInterfaceOverChannel │ │ │ └── prog1.go │ └── Preprocess │ │ ├── EvenLockAndUnlockAndLockComesLater │ │ └── EvenLockAndUnlockAndLockComesLater.go │ │ ├── LockAndUnlockInDifferentBranches │ │ └── LockAndUnlockInDifferentBranches.go │ │ ├── LockInAStruct │ │ └── LockInAStruct.go │ │ ├── LockInEmbeddedStruct │ │ └── LockInEmbeddedStruct.go │ │ ├── MutexInterface │ │ └── MutexInterface.go │ │ ├── NestedFunctionWithLock │ │ └── NestedFunctionWithLock.go │ │ ├── NestedFunctionWithLockButThenUnlock │ │ └── NestedFunctionWithLockButThenUnlock.go │ │ ├── RecursionNestedFunctionWithLock │ │ └── RecursionNestedFunctionWithLock.go │ │ ├── UnevenLockAndUnlockAndLockComesLater │ │ └── UnevenLockAndUnlockAndLockComesLater.go │ │ ├── functionWithEvenLockAndDeferUnlock │ │ └── functionWithEvenLockAndDeferUnlock.go │ │ ├── functionWithEvenLockAndUnlock │ │ └── functionWithEvenLockAndUnlock.go │ │ ├── functionWithLock │ │ └── functionWithLock.go │ │ ├── functionWithUnevenLockAndUnlock │ │ └── functionWithUnevenLockAndUnlock.go │ │ ├── functionWithUnlock │ │ └── functionWithUnlock.go │ │ └── functionWithoutLocks │ │ └── functionWithoutLocks.go └── testutils.go ├── tests ├── stdlib_runner_test.go └── testdata │ ├── stdlib │ ├── RaceNestedArrayCopy │ │ └── prog1.go │ ├── TestNoRaceAddrExpr │ │ └── prog1.go │ ├── TestNoRaceAsFunc4 │ │ └── prog1.go │ ├── TestNoRaceCase │ │ └── prog1.go │ ├── TestNoRaceComp │ │ └── prog1.go │ ├── TestNoRaceEnoughRegisters │ │ └── prog1.go │ ├── TestNoRaceFuncArgsRW │ │ └── prog1.go │ ├── TestNoRaceHeapReallocation │ │ └── prog1.go │ ├── TestNoRaceIntRWClosures │ │ └── prog1.go │ ├── TestNoRaceMethodThunk │ │ └── prog1.go │ ├── TestNoRaceMethodValue │ │ └── prog1.go │ ├── TestNoRacePlus │ │ └── prog1.go │ ├── TestNoRaceRangeIssue5446 │ │ └── prog1.go │ ├── TestNoRaceSelect4 │ │ └── prog1.go │ ├── TestNoRaceStackPushPop │ │ └── prog1.go │ ├── TestNoRaceStructFieldRW1 │ │ └── prog1.go │ ├── TestNoRaceStructFieldRW2 │ │ └── prog1.go │ ├── TestNoRaceTinyAlloc │ │ └── prog1.go │ ├── TestRaceAddrExpr │ │ └── prog1.go │ ├── TestRaceAnd │ │ └── prog1.go │ ├── TestRaceAnd2 │ │ └── prog1.go │ ├── TestRaceAppendCapRW │ │ └── prog1.go │ ├── TestRaceAppendLenRW │ │ └── prog1.go │ ├── TestRaceAppendRW │ │ └── prog1.go │ ├── TestRaceArrayCopy │ │ └── prog1.go │ ├── TestRaceArrayInit │ │ └── prog1.go │ ├── TestRaceAsFunc1 │ │ └── prog1.go │ ├── TestRaceAsFunc2 │ │ └── prog1.go │ ├── TestRaceAsFunc3 │ │ └── prog1.go │ ├── TestRaceBlockAs │ │ └── prog1.go │ ├── TestRaceCaseBody │ │ └── prog1.go │ ├── TestRaceCaseCondition │ │ └── prog1.go │ ├── TestRaceCaseCondition2 │ │ └── prog1.go │ ├── TestRaceCaseFallthrough │ │ └── prog1.go │ ├── TestRaceCaseIssue6418 │ │ └── prog1.go │ ├── TestRaceCaseType │ │ └── prog1.go │ ├── TestRaceCaseTypeBody │ │ └── prog1.go │ ├── TestRaceCaseTypeIssue5890 │ │ └── prog1.go │ ├── TestRaceComp2 │ │ └── prog1.go │ ├── TestRaceComplement │ │ └── prog1.go │ ├── TestRaceComplex128WW │ │ └── prog1.go │ ├── TestRaceDeferArg │ │ └── prog1.go │ ├── TestRaceDeferArg2 │ │ └── prog1.go │ ├── TestRaceDiv │ │ └── prog1.go │ ├── TestRaceDivConst │ │ └── prog1.go │ ├── TestRaceEfaceConv │ │ └── prog1.go │ ├── TestRaceEfaceWW │ │ └── prog1.go │ ├── TestRaceEmptyInterface │ │ └── prog1.go │ ├── TestRaceEmptyInterface2 │ │ └── prog1.go │ ├── TestRaceError │ │ └── prog1.go │ ├── TestRaceFloat64WW │ │ └── prog1.go │ ├── TestRaceForIncr │ │ └── prog1.go │ ├── TestRaceForInit │ │ └── prog1.go │ ├── TestRaceForTest │ │ └── prog1.go │ ├── TestRaceFuncArgsRW │ │ └── prog1.go │ ├── TestRaceFuncArgument │ │ └── prog1.go │ ├── TestRaceFuncArgument2 │ │ └── prog1.go │ ├── TestRaceFuncCall │ │ └── prog1.go │ ├── TestRaceFuncItself │ │ └── prog1.go │ ├── TestRaceFuncVariableRW │ │ └── prog1.go │ ├── TestRaceFuncVariableWW │ │ └── prog1.go │ ├── TestRaceIfaceCmp │ │ └── prog1.go │ ├── TestRaceIfaceCmpNil │ │ └── prog1.go │ ├── TestRaceIfaceConv │ │ └── prog1.go │ ├── TestRaceIfaceWW │ │ └── prog1.go │ ├── TestRaceInt32RWClosures │ │ └── prog1.go │ ├── TestRaceIntRWClosures │ │ └── prog1.go │ ├── TestRaceIntRWGlobalFuncs │ │ └── prog1.go │ ├── TestRaceInterCall │ │ └── prog1.go │ ├── TestRaceInterCall2 │ │ └── prog1.go │ ├── TestRaceIntptrRW │ │ └── prog1.go │ ├── TestRaceIssue5564 │ │ └── prog1.go │ ├── TestRaceIssue5567 │ │ └── prog1.go │ ├── TestRaceMapInit │ │ └── prog1.go │ ├── TestRaceMapInit2 │ │ └── prog1.go │ ├── TestRaceMethodCall │ │ └── prog1.go │ ├── TestRaceMethodCall2 │ │ └── prog1.go │ ├── TestRaceMethodThunk2 │ │ └── prog1.go │ ├── TestRaceMethodThunk3 │ │ └── prog1.go │ ├── TestRaceMethodValue │ │ └── prog1.go │ ├── TestRaceMethodValue2 │ │ └── prog1.go │ ├── TestRaceMethodValue3 │ │ └── prog1.go │ ├── TestRaceMod │ │ └── prog1.go │ ├── TestRaceModConst │ │ └── prog1.go │ ├── TestRaceNestedStruct │ │ └── prog1.go │ ├── TestRaceOr │ │ └── prog1.go │ ├── TestRaceOr2 │ │ └── prog1.go │ ├── TestRacePanicArg │ │ └── prog1.go │ ├── TestRacePlus │ │ └── prog1.go │ ├── TestRacePlus2 │ │ └── prog1.go │ ├── TestRaceRotate │ │ └── prog1.go │ ├── TestRaceRune │ │ └── prog1.go │ ├── TestRaceSelect1 │ │ └── prog1.go │ ├── TestRaceSelect2 │ │ └── prog1.go │ ├── TestRaceSelect3 │ │ └── prog1.go │ ├── TestRaceSelect4 │ │ └── prog1.go │ ├── TestRaceSelect5 │ │ └── prog1.go │ ├── TestRaceSliceSlice │ │ └── prog1.go │ ├── TestRaceSliceSlice2 │ │ └── prog1.go │ ├── TestRaceSliceString │ │ └── prog1.go │ ├── TestRaceSprint │ │ └── prog1.go │ ├── TestRaceStringPtrRW │ │ └── prog1.go │ ├── TestRaceStringRW │ │ └── prog1.go │ ├── TestRaceStructFieldRW1 │ │ └── prog1.go │ ├── TestRaceStructFieldRW2 │ │ └── prog1.go │ ├── TestRaceStructFieldRW3 │ │ └── prog1.go │ ├── TestRaceStructInd │ │ └── prog1.go │ ├── TestRaceStructInit │ │ └── prog1.go │ ├── TestRaceTLS │ │ └── prog1.go │ ├── TestRaceTypeAssert │ │ └── prog1.go │ ├── TestRaceUnaddressableMapLen │ │ └── prog1.go │ └── TestRaceUnsafePtrRW │ │ └── prog1.go │ └── stdlibNoSuccess │ ├── TestNoRaceAnd │ └── prog1.go │ ├── TestNoRaceBlank │ └── prog1.go │ ├── TestNoRaceCaseFallthrough │ └── prog1.go │ ├── TestNoRaceEmptyStruct │ └── prog1.go │ ├── TestNoRaceForIncr │ └── prog1.go │ ├── TestNoRaceForInit │ └── prog1.go │ ├── TestNoRaceFuncUnlock │ └── prog1.go │ ├── TestNoRaceOr │ └── prog1.go │ ├── TestNoRaceSelect1 │ └── prog1.go │ ├── TestNoRaceSelect2 │ └── prog1.go │ ├── TestNoRaceSelect3 │ └── prog1.go │ ├── TestNoRaceSelect5 │ └── prog1.go │ ├── TestNoRaceShortCalc │ └── prog1.go │ ├── TestNoRaceShortCalc2 │ └── prog1.go │ ├── TestRaceAppendSliceStruct │ └── prog1.go │ ├── TestRaceHeapParam │ └── prog1.go │ ├── TestRaceIndirection │ └── prog1.go │ ├── TestRaceMethodThunk │ └── prog1.go │ ├── TestRaceMethodThunk4 │ └── prog1.go │ ├── TestRacePanic │ └── prog1.go │ ├── TestRaceRange │ └── prog1.go │ ├── TestRaceSliceStruct │ └── prog1.go │ └── TestRaceStructRW │ └── prog1.go └── utils ├── DoubleKeyMap.go ├── counter.go ├── set.go ├── stacks ├── basicBlockStack.go ├── callCommonStack.go ├── functionStack.go └── intStack.go └── util.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Golang CircleCI 2.0 configuration file 2 | version: 2 3 | jobs: 4 | build: 5 | docker: 6 | - image: circleci/golang:1.15 7 | working_directory: /go/src/github.com/amit-davidson/Chronos 8 | steps: 9 | - checkout 10 | - run: 11 | name: Install dependencies 12 | command: | 13 | go get -v -t -d ./... 14 | - run: 15 | name: Run tests 16 | command: | 17 | go test -v ./... -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.so 4 | .idea 5 | .vscode 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Amit Davidson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chronos 2 | 3 |

4 | 5 |

6 | 7 | [![made-with-Go](https://github.com/go-critic/go-critic/workflows/Go/badge.svg)](http://golang.org) 8 | [![made-with-Go](https://img.shields.io/badge/Made%20with-Go-1f425f.svg)](http://golang.org) 9 | [![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://lbesson.mit-license.org/) 10 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) 11 | [![amit-davidson](https://circleci.com/gh/amit-davidson/Chronos.svg?style=svg)](https://app.circleci.com/pipelines/github/amit-davidson/Chronos) 12 | 13 | Chronos is a static race detector for the Go language written in Go. 14 | 15 | ## Quick Start: 16 | 17 | Download the package 18 | 19 | ``` 20 | go get -v github.com/amit-davidson/Chronos/cmd/chronos 21 | ``` 22 | 23 | Pass the entry point 24 | 25 | ``` 26 | chronos --file --mod 27 | ``` 28 | 29 | Help 30 | 31 | ``` 32 | Usage of ./chronos: 33 | --file string 34 | The file containing the entry point of the program 35 | --mod string 36 | Absolute or relative path to the module where the search should be performed. Should end in the format:{VCS}/{organization}/{package}. Packages outside this path are excluded rom the search. 37 | ``` 38 | 39 | ## Example: 40 | 41 |

42 | 43 | 44 |

45 | 46 | ## Features: 47 | 48 | Support: 49 | 50 | - Detects races on pointers passed around the program. 51 | - Analysis of conditional branches, nested functions, interfaces, select, gotos, defers, for loops and recursions. 52 | - Synchronization using mutex and goroutines starts. 53 | 54 | Limitations: 55 | 56 | - Big programs and external packages. (Due to stack overflow) 57 | - Synchronization using channels, waitgroups, once, cond and atomic. 58 | 59 | ## Chronos vs go race: 60 | 61 | Chronos successfully reports cases where go race fails thanks to it's static nature. Mostly because data races appear in unexpected production workloads, which are hard to produce in dev. 62 | In addition, go race is having trouble with short programs where without contrived synchronization the program may exit too quickly. 63 | 64 | In contrast, Chronos managed to report only 244/403 = 60.5% of go race test cases. This can be explained by Chronos partial support with Go's features so this number will increase in the future. 65 | Also, it lacked due to his static nature where context/path sensitivity was required. 66 | 67 | Therefore, I suggest using both according the strengths and weaknesses of each of the race detectors. 68 | 69 | ## Credits: 70 | 71 | Jan Wen, J., Jhala, R., & Lerner, S. (n.d.). [RELAY: Static Race Detection on Millions of Lines of Code](https://cseweb.ucsd.edu/~lerner/papers/relay.pdf) 72 | Colin J. Fidge (February 1988). [Timestamps in Message-Passing Systems That Preserve the Partial Ordering"](http://zoo.cs.yale.edu/classes/cs426/2012/lab/bib/fidge88timestamps.pdf) 73 | 74 | ## More examples: 75 | 76 |

77 | 78 | 79 |

80 |
81 | -------------------------------------------------------------------------------- /cmd/chronos/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/amit-davidson/Chronos/domain" 7 | "github.com/amit-davidson/Chronos/output" 8 | "github.com/amit-davidson/Chronos/pointerAnalysis" 9 | "github.com/amit-davidson/Chronos/ssaUtils" 10 | "github.com/amit-davidson/Chronos/utils" 11 | "golang.org/x/tools/go/ssa" 12 | "os" 13 | ) 14 | 15 | func main() { 16 | defaultFile := flag.String("file", "", "The file containing the entry point of the program") 17 | defaultModulePath := flag.String("mod", "", "PPath to the module where the search should be performed. Path to module can be relative or absolute but must contain the format:{VCS}/{organization}/{package}. Packages outside this path are excluded rom the search.") 18 | flag.Parse() 19 | if *defaultFile == "" { 20 | fmt.Printf("Please provide a file to load\n") 21 | os.Exit(1) 22 | } 23 | if *defaultModulePath == "" { 24 | fmt.Printf("Please provide a path to the module. path to module can be relative or absolute but must contain the format:{VCS}/{organization}/{package}.\n") 25 | os.Exit(1) 26 | } 27 | domain.GoroutineCounter = utils.NewCounter() 28 | domain.GuardedAccessCounter = utils.NewCounter() 29 | domain.PosIDCounter = utils.NewCounter() 30 | 31 | ssaProg, ssaPkg, err := ssaUtils.LoadPackage(*defaultFile, *defaultModulePath) 32 | if err != nil { 33 | fmt.Printf("Failed loading with the following error:%s\n", err) 34 | os.Exit(1) 35 | } 36 | entryFunc := ssaPkg.Func("main") 37 | err = ssaUtils.InitPreProcess(ssaProg, *defaultModulePath) 38 | if err != nil { 39 | fmt.Print(err) 40 | os.Exit(1) 41 | } 42 | 43 | entryCallCommon := ssa.CallCommon{Value: entryFunc} 44 | functionState := ssaUtils.HandleCallCommon(domain.NewEmptyContext(), &entryCallCommon, entryFunc.Pos()) 45 | conflictingGAs, err := pointerAnalysis.Analysis(ssaPkg, functionState.GuardedAccesses) 46 | if err != nil { 47 | fmt.Printf("Error in analysis:%s\n", err) 48 | os.Exit(1) 49 | } 50 | err = output.GenerateError(conflictingGAs, ssaProg) 51 | if err != nil { 52 | fmt.Printf("Error in generating errors:%s\n", err) 53 | os.Exit(1) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /domain/BlockState.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import "github.com/amit-davidson/Chronos/utils/stacks" 4 | 5 | type BlockState struct { 6 | GuardedAccesses []*GuardedAccess 7 | Lockset *Lockset 8 | DeferredFunctions *stacks.CallCommonStack 9 | } 10 | 11 | func GetEmptyBlockState() *BlockState { 12 | return &BlockState{ 13 | GuardedAccesses: make([]*GuardedAccess, 0), 14 | Lockset: NewLockset(), 15 | DeferredFunctions: stacks.NewCallCommonStack(), 16 | } 17 | } 18 | 19 | func CreateBlockState(ga []*GuardedAccess, ls *Lockset, df *stacks.CallCommonStack) *BlockState { 20 | return &BlockState{ 21 | GuardedAccesses: ga, 22 | Lockset: ls, 23 | DeferredFunctions: df, 24 | } 25 | } 26 | 27 | // AddFunctionCallState is used to add the state of a function call to the blocks total state when iterating through it. 28 | // shouldMergeLockset is used depending if the call was using a goroutine or not. 29 | func (existingBlock *BlockState) AddFunctionCallState(newBlock *BlockState, shouldMergeLockset bool) { 30 | for _, guardedAccess := range newBlock.GuardedAccesses { 31 | guardedAccess.Lockset.UpdateWithPrevLockset(existingBlock.Lockset) 32 | 33 | } 34 | existingBlock.GuardedAccesses = append(existingBlock.GuardedAccesses, newBlock.GuardedAccesses...) 35 | if shouldMergeLockset { 36 | existingBlock.Lockset.UpdateWithNewLockSet(newBlock.Lockset.Locks, newBlock.Lockset.Unlocks) 37 | } 38 | } 39 | 40 | // MergeChildBlock merges child block with it's parent in append-like fashion. 41 | // A -> B 42 | // Will Merge B unto A 43 | func (existingBlock *BlockState) MergeChildBlock(newBlock *BlockState) { 44 | for _, guardedAccess := range newBlock.GuardedAccesses { 45 | guardedAccess.Lockset.UpdateWithPrevLockset(existingBlock.Lockset) 46 | } 47 | existingBlock.GuardedAccesses = append(existingBlock.GuardedAccesses, newBlock.GuardedAccesses...) 48 | existingBlock.DeferredFunctions.MergeStacks(newBlock.DeferredFunctions) 49 | existingBlock.Lockset.UpdateWithNewLockSet(newBlock.Lockset.Locks, newBlock.Lockset.Unlocks) 50 | } 51 | 52 | // MergeSiblingBlock merges sibling blocks in merge-like fashion. 53 | // A -> B 54 | // -> C 55 | // Will Merge B and C 56 | func (existingBlock *BlockState) MergeSiblingBlock(newBlock *BlockState) { 57 | existingGAs := make(map[int]*GuardedAccess, len(existingBlock.GuardedAccesses)) 58 | for _, ga := range existingBlock.GuardedAccesses { 59 | existingGAs[ga.ID] = ga 60 | } 61 | 62 | for _, newGA := range newBlock.GuardedAccesses { 63 | if existingGA, ok := existingGAs[newGA.ID]; !ok { 64 | existingBlock.GuardedAccesses = append(existingBlock.GuardedAccesses, newGA) 65 | } else { 66 | existingGA.Lockset.MergeSiblingLockset(newGA.Lockset) 67 | } 68 | } 69 | 70 | existingBlock.Lockset.MergeSiblingLockset(newBlock.Lockset) 71 | } 72 | 73 | func (existingBlock *BlockState) Copy() *BlockState { 74 | newFunctionState := &BlockState{} 75 | newFunctionState.Lockset = existingBlock.Lockset.Copy() 76 | for _, ga := range existingBlock.GuardedAccesses { 77 | newFunctionState.GuardedAccesses = append(newFunctionState.GuardedAccesses, ga.Copy()) 78 | } 79 | newFunctionState.DeferredFunctions = existingBlock.DeferredFunctions 80 | return newFunctionState 81 | } 82 | -------------------------------------------------------------------------------- /domain/Context.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import ( 4 | "github.com/amit-davidson/Chronos/utils/stacks" 5 | ) 6 | 7 | // Flow context 8 | type Context struct { 9 | GoroutineID int 10 | Clock VectorClock 11 | StackTrace *stacks.IntStackWithMap 12 | } 13 | 14 | func NewEmptyContext() *Context { 15 | return &Context{ 16 | Clock: VectorClock{}, 17 | GoroutineID: GoroutineCounter.GetNext(), 18 | StackTrace: stacks.NewEmptyIntStackWithMap(), 19 | } 20 | } 21 | 22 | func NewGoroutineExecutionState(state *Context) *Context { 23 | state.Increment() 24 | return &Context{ 25 | Clock: state.Clock.Copy(), 26 | GoroutineID: GoroutineCounter.GetNext(), 27 | StackTrace: state.StackTrace.Copy(), 28 | } 29 | } 30 | 31 | func (gs *Context) Increment() { 32 | gs.Clock[gs.GoroutineID] += 1 33 | } 34 | 35 | func (gs *Context) MayConcurrent(state *Context) bool { 36 | timestampAidA := gs.Clock.Get(gs.GoroutineID) 37 | timestampAidB := state.Clock.Get(gs.GoroutineID) 38 | timestampBidA := gs.Clock.Get(state.GoroutineID) 39 | timestampBidB := state.Clock.Get(state.GoroutineID) 40 | isBefore := timestampAidA <= timestampAidB && timestampBidA < timestampBidB 41 | isAfter := timestampBidB <= timestampBidA && timestampAidB < timestampAidA 42 | return !(isBefore || isAfter) 43 | } 44 | 45 | func (gs *Context) Copy() *Context { 46 | return &Context{ 47 | GoroutineID: gs.GoroutineID, 48 | Clock: gs.Clock.Copy(), 49 | StackTrace: gs.StackTrace.Copy(), 50 | } 51 | } 52 | 53 | func (gs *Context) CopyWithoutMap() *Context { 54 | return &Context{ 55 | GoroutineID: gs.GoroutineID, 56 | Clock: gs.Clock.Copy(), 57 | StackTrace: stacks.NewIntStackWithMap(*gs.StackTrace.GetItems().Copy(), nil), 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /domain/DeferFunction.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import "golang.org/x/tools/go/ssa" 4 | 5 | type DeferFunction struct { 6 | Function *ssa.CallCommon 7 | BlockIndex int 8 | } 9 | -------------------------------------------------------------------------------- /domain/FunctionState.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import ( 4 | "github.com/amit-davidson/Chronos/utils" 5 | "github.com/amit-davidson/Chronos/utils/stacks" 6 | ) 7 | 8 | var GoroutineCounter *utils.Counter 9 | var GuardedAccessCounter *utils.Counter 10 | var PosIDCounter *utils.Counter 11 | 12 | type FunctionState struct { 13 | GuardedAccesses []*GuardedAccess 14 | Lockset *Lockset 15 | } 16 | 17 | func GetFunctionState() *FunctionState { 18 | return &FunctionState{ 19 | GuardedAccesses: make([]*GuardedAccess, 0), 20 | Lockset: NewLockset(), 21 | } 22 | } 23 | 24 | func CreateFunctionState(ga []*GuardedAccess, ls *Lockset) *FunctionState { 25 | return &FunctionState{ 26 | GuardedAccesses: ga, 27 | Lockset: ls, 28 | } 29 | } 30 | 31 | // AddContextToFunction adds flow specific context data 32 | func (fs *FunctionState) AddContextToFunction(context *Context) { 33 | for _, ga := range fs.GuardedAccesses { 34 | ga.ID = GuardedAccessCounter.GetNext() 35 | ga.State.GoroutineID = context.GoroutineID 36 | context.Increment() 37 | 38 | relativePos := ga.State.StackTrace.Iter()[ga.PosToRemove+1:] 39 | tmpContext := context.CopyWithoutMap() 40 | tmpContext.StackTrace.GetItems().MergeStacks((*stacks.IntStack)(&relativePos)) 41 | ga.State.StackTrace = tmpContext.StackTrace 42 | ga.State.Clock = tmpContext.Clock 43 | } 44 | } 45 | 46 | // RemoveContextFromFunction strips any context related data from the guarded access fields. It nullifies id, goroutine id, 47 | // clock and removes from the guarded access the prefix that matches the context path. This way, other flows can take 48 | // the guarded access and add relevant data. 49 | func (fs *FunctionState) RemoveContextFromFunction() { 50 | gas := make([]*GuardedAccess, 0, len(fs.GuardedAccesses)) 51 | for i := range fs.GuardedAccesses { 52 | ga := fs.GuardedAccesses[i].ShallowCopy() 53 | ga.PosToRemove++ 54 | gas = append(gas, ga) 55 | } 56 | fs.GuardedAccesses = gas 57 | } 58 | 59 | func (fs *FunctionState) Copy() *FunctionState { 60 | newFunctionState := GetFunctionState() 61 | newFunctionState.Lockset = fs.Lockset.Copy() 62 | for _, ga := range fs.GuardedAccesses { 63 | newFunctionState.GuardedAccesses = append(newFunctionState.GuardedAccesses, ga.Copy()) 64 | } 65 | return newFunctionState 66 | } 67 | -------------------------------------------------------------------------------- /domain/GuardedAccess.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import ( 4 | "github.com/amit-davidson/Chronos/ssaPureUtils" 5 | "go/token" 6 | "golang.org/x/tools/go/ssa" 7 | ) 8 | 9 | type OpKind int 10 | 11 | const ( 12 | GuardAccessRead OpKind = iota 13 | GuardAccessWrite 14 | ) 15 | 16 | func (op OpKind) String() string { 17 | switch op { 18 | case GuardAccessRead: 19 | return "Read" 20 | case GuardAccessWrite: 21 | return "Write" 22 | default: 23 | return "Unknown op type" 24 | } 25 | } 26 | 27 | type GuardedAccess struct { 28 | *PosData 29 | *FlowData 30 | } 31 | 32 | type PosData struct { 33 | PosID int // guarded accesses of the same function share the same PosID. It's used to mark the same guarded access in different flows. 34 | Pos token.Pos 35 | OpKind OpKind 36 | Value ssa.Value 37 | } 38 | 39 | type FlowData struct { 40 | PosToRemove int 41 | ID int // ID depends on the flow, which means it's unique. 42 | State *Context 43 | Lockset *Lockset 44 | } 45 | 46 | func (ga *FlowData) Copy() *FlowData { 47 | return &FlowData{ 48 | ID: ga.ID, 49 | PosToRemove: ga.PosToRemove, 50 | Lockset: ga.Lockset.Copy(), 51 | State: ga.State.CopyWithoutMap(), 52 | } 53 | } 54 | 55 | func (ga *GuardedAccess) Copy() *GuardedAccess { 56 | return &GuardedAccess{ 57 | PosData: ga.PosData, 58 | FlowData: ga.FlowData.Copy(), 59 | } 60 | } 61 | 62 | func (ga *GuardedAccess) ShallowCopy() *GuardedAccess { 63 | return &GuardedAccess{ 64 | PosData: ga.PosData, 65 | FlowData: ga.FlowData.Copy(), 66 | } 67 | } 68 | 69 | func (ga *GuardedAccess) Intersects(gaToCompare *GuardedAccess) bool { 70 | if ga.ID == gaToCompare.ID || ga.State.GoroutineID == gaToCompare.State.GoroutineID { 71 | return true 72 | } 73 | if ga.OpKind == GuardAccessRead && gaToCompare.OpKind == GuardAccessRead { 74 | return true 75 | } 76 | 77 | if ssaPureUtils.FilterStructs(ga.Value, gaToCompare.Value) { 78 | return true 79 | } 80 | 81 | for lockA := range ga.Lockset.Locks { 82 | for lockB := range gaToCompare.Lockset.Locks { 83 | if lockA == lockB { 84 | return true 85 | } 86 | } 87 | } 88 | return false 89 | } 90 | 91 | func (ga *GuardedAccess) IsConflicting(gaToCompare *GuardedAccess) bool { 92 | return !ga.Intersects(gaToCompare) && ga.State.MayConcurrent(gaToCompare.State) 93 | } 94 | 95 | func AddGuardedAccess(pos token.Pos, value ssa.Value, kind OpKind, lockset *Lockset, context *Context) *GuardedAccess { 96 | context.Increment() 97 | return &GuardedAccess{ 98 | PosData: &PosData{ 99 | PosID: PosIDCounter.GetNext(), 100 | Pos: pos, 101 | OpKind: kind, 102 | Value: value, 103 | }, 104 | FlowData: &FlowData{ 105 | ID: GuardedAccessCounter.GetNext(), 106 | Lockset: lockset.Copy(), 107 | State: context.CopyWithoutMap(), 108 | }, 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /domain/Lockset.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import ( 4 | "go/token" 5 | 6 | "golang.org/x/tools/go/ssa" 7 | ) 8 | 9 | type locksLastUse map[token.Pos]*ssa.CallCommon 10 | 11 | type Lockset struct { 12 | Locks locksLastUse 13 | Unlocks locksLastUse 14 | } 15 | 16 | func NewLockset() *Lockset { 17 | return &Lockset{ 18 | Locks: make(locksLastUse), 19 | Unlocks: make(locksLastUse), 20 | } 21 | } 22 | 23 | // The three following methods handle updating the lockset the same way. By recording each lock state (locked/unlocked) 24 | // at the current point. It means that if a mutex was unlocked at some point but later was locked again, then it's latest 25 | // status is locked, and the unlock status is removed. The difference between each algorithm is the context used. 26 | // 27 | // UpdateWithNewLockSet updates the lockset and expects newLocks, newUnlocks to contain the most up to date status of the 28 | // mutex and update accordingly. 29 | func (ls *Lockset) UpdateWithNewLockSet(newLocks, newUnlocks locksLastUse) { 30 | for lockName, lock := range newLocks { 31 | ls.Locks[lockName] = lock 32 | } 33 | for unlockName := range newUnlocks { 34 | delete(ls.Locks, unlockName) 35 | } 36 | for unlockName, unlock := range newUnlocks { 37 | ls.Unlocks[unlockName] = unlock 38 | } 39 | for lockName := range newLocks { 40 | if _, ok := ls.Locks[lockName]; ok { 41 | delete(ls.Unlocks, lockName) 42 | } 43 | } 44 | } 45 | 46 | // UpdateWithPrevLockset works the same as UpdateWithNewLockSet but expects prevLS to contain an earlier version of the 47 | // status of the locks. 48 | func (ls *Lockset) UpdateWithPrevLockset(prevLS *Lockset) { 49 | for lockName, lock := range prevLS.Locks { 50 | _, okLock := ls.Locks[lockName] // We check to see the lock doesn't exist to not override it with old reference of this lock 51 | _, okUnlock := ls.Unlocks[lockName] 52 | if !okLock && !okUnlock { 53 | ls.Locks[lockName] = lock 54 | } 55 | } 56 | 57 | for lockName, lock := range prevLS.Unlocks { 58 | _, okLock := ls.Locks[lockName] // We check to see the unlock doesn't exist to not override it with old reference of this unlock 59 | _, okUnlock := ls.Unlocks[lockName] 60 | if !okLock && !okUnlock { 61 | ls.Unlocks[lockName] = lock 62 | } 63 | } 64 | } 65 | 66 | // MergeSiblingLockset is called when merging different paths of the control flow graph. The mutex status should be 67 | // merged and not appended. Because Locks is a must set, for a lock to appear in the result, an intersect 68 | // between the branches' lockset is performed to make sure the lock appears in all branches. Unlock is a may set, so a 69 | // union is applied since it's sufficient to have an unlock at least in one of the branches. 70 | func (ls *Lockset) MergeSiblingLockset(locksetToMerge *Lockset) { 71 | ls.Locks.intersect(locksetToMerge.Locks) 72 | ls.Unlocks.union(locksetToMerge.Unlocks) 73 | 74 | for unlockName := range ls.Unlocks { 75 | // If there's a lock in one branch and an unlock in second, then unlock wins 76 | delete(ls.Locks, unlockName) 77 | } 78 | } 79 | 80 | func (ls *Lockset) Copy() *Lockset { 81 | newLs := &Lockset{} 82 | newLocks := make(locksLastUse, len(ls.Locks)) 83 | for key, value := range ls.Locks { 84 | newLocks[key] = value 85 | } 86 | newLs.Locks = newLocks 87 | newUnlocks := make(locksLastUse, len(ls.Unlocks)) 88 | for key, value := range ls.Unlocks { 89 | newUnlocks[key] = value 90 | } 91 | newLs.Unlocks = newUnlocks 92 | return newLs 93 | } 94 | 95 | func (ls *locksLastUse) intersect(newLS locksLastUse) { 96 | found := false 97 | for a := range *ls { 98 | for b := range newLS { 99 | if a == b { 100 | found = true 101 | } 102 | } 103 | if !found { 104 | delete(*ls, a) 105 | } 106 | } 107 | } 108 | 109 | func (ls *locksLastUse) union(newLS locksLastUse) { 110 | for b := range newLS { 111 | (*ls)[b] = newLS[b] 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /domain/VectorClock.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import ( 4 | "math" 5 | ) 6 | 7 | type VectorClock map[int]int 8 | 9 | func (vc VectorClock) MergeClocks(clockToMerge VectorClock) { 10 | for goroutine, existingTimestamp := range clockToMerge { 11 | newTimestamp := vc.Get(goroutine) // 0 as the default value, or the actual value 12 | vc[goroutine] = int(math.Max(float64(existingTimestamp), float64(newTimestamp))) 13 | } 14 | } 15 | 16 | // Get returns clock for provided goroutine ID. 17 | // If no such goroutine or clock is not initialized, 18 | // then returns a zero value. 19 | func (vc VectorClock) Get(id int) int { 20 | if vc == nil { 21 | return 0 22 | } 23 | var ts, _ = vc[id] 24 | return ts 25 | } 26 | 27 | func (vc VectorClock) Copy() VectorClock { 28 | copiedClock := make(map[int]int, len(vc)) 29 | for goroutine, timestamp := range vc { 30 | copiedClock[goroutine] = timestamp 31 | } 32 | return copiedClock 33 | } 34 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/amit-davidson/Chronos 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/stretchr/testify v1.6.1 7 | golang.org/x/tools v0.0.0-20201020161133-226fd2f889ca 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 6 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 7 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 8 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 9 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 10 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 11 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 12 | golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= 13 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 14 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 15 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 16 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 17 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 18 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 19 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 20 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 21 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 22 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 23 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 24 | golang.org/x/tools v0.0.0-20201020161133-226fd2f889ca h1:pvScuB+UnCGDas2naNKUOXruM08MjwVcEdaweeynIqQ= 25 | golang.org/x/tools v0.0.0-20201020161133-226fd2f889ca/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= 26 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 27 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 28 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 29 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 30 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 31 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 32 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 33 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 34 | -------------------------------------------------------------------------------- /output/warnings.go: -------------------------------------------------------------------------------- 1 | package output 2 | 3 | import ( 4 | "fmt" 5 | "github.com/amit-davidson/Chronos/domain" 6 | "github.com/amit-davidson/Chronos/pointerAnalysis" 7 | "github.com/amit-davidson/Chronos/ssaUtils" 8 | "github.com/amit-davidson/Chronos/utils" 9 | "golang.org/x/tools/go/ssa" 10 | "strings" 11 | "unicode" 12 | ) 13 | 14 | const ( 15 | spacePrefixCount = 8 16 | ) 17 | 18 | func GenerateError(conflictingGAs [][]*domain.GuardedAccess, prog *ssa.Program) error { 19 | if len(conflictingGAs) == 0 { 20 | print("No data races found\n") 21 | return nil 22 | } 23 | filteredDuplicates := pointerAnalysis.FilterDuplicates(conflictingGAs) 24 | messages := make([]string, 0) 25 | for _, conflict := range filteredDuplicates { 26 | label, err := getMessage(conflict[0], conflict[1], prog) 27 | if err != nil { 28 | return err 29 | } 30 | messages = append(messages, label) 31 | } 32 | print(messages[0]) 33 | for _, message := range messages[1:] { 34 | print("=========================\n") 35 | print(message) 36 | } 37 | 38 | return nil 39 | } 40 | 41 | func getMessage(guardedAccessA, guardedAccessB *domain.GuardedAccess, prog *ssa.Program) (string, error) { 42 | message := "Potential race condition:\n" 43 | messageA, err := getMessageByLine(guardedAccessA, prog) 44 | if err != nil { 45 | return "", err 46 | } 47 | messageB, err := getMessageByLine(guardedAccessB, prog) 48 | if err != nil { 49 | return "", err 50 | } 51 | message += fmt.Sprintf(" %s:\n%s\n \n %s:\n%s \n", "Access1", messageA, "Access2", messageB) 52 | return message, nil 53 | } 54 | 55 | func getMessageByLine(guardedAccessA *domain.GuardedAccess, prog *ssa.Program) (string, error) { 56 | message := "" 57 | posA := prog.Fset.Position(guardedAccessA.Pos) 58 | lineA, err := utils.ReadLineByNumber(posA.Filename, posA.Line) 59 | trimmedA := strings.TrimLeftFunc(lineA, unicode.IsSpace) 60 | message += strings.Repeat(" ", spacePrefixCount) + trimmedA 61 | 62 | removedSpaces := len(lineA) - len(trimmedA) 63 | posToAddArrow := posA.Column - removedSpaces 64 | message += "\n" + strings.Repeat(" ", posToAddArrow+spacePrefixCount-1) + "^" + "\n" 65 | stackA := ssaUtils.GetStackTrace(prog, guardedAccessA) 66 | if err != nil { 67 | return "", err 68 | } 69 | stackA += posA.String() 70 | message += stackA 71 | return message, nil 72 | } 73 | -------------------------------------------------------------------------------- /pointerAnalysis/PointerAnalysis.go: -------------------------------------------------------------------------------- 1 | package pointerAnalysis 2 | 3 | import ( 4 | "github.com/amit-davidson/Chronos/domain" 5 | "github.com/amit-davidson/Chronos/utils" 6 | "go/token" 7 | "golang.org/x/tools/go/pointer" 8 | "golang.org/x/tools/go/ssa" 9 | ) 10 | 11 | 12 | // Analysis starts by mapping between positions of the guard accesses (values inside) to the guard accesses themselves. 13 | // Then it analyzes all the values inside values inside, and check if some of the values might alias each other. If so, 14 | // the positions for those values are merged. After all positions were merged, the algorithm runs and check if for a 15 | // given value (identified by a pos in the map) there are two guarded accesses that might conflict - W&W/R&W from two 16 | // different goroutines. 17 | // map1 : A->ga1, ga2, ga3 18 | // B->ga4, ga5, ga6 19 | // C->ga7, ga8, ga9 20 | // D->ga10, ga11, ga12 21 | // Now that we know that B may point to A, we add it to it 22 | // map1 : A->ga1, ga2, ga3, ga4, ga5, ga6 23 | // C->ga7, ga8, ga9 24 | // D->ga10, ga11, ga12 25 | // And if A may point to D, then 26 | // map1 : C->ga7, ga8, ga9 27 | // D->ga10, ga11, ga12, ga1, ga2, ga3, ga4, ga5, ga6 28 | // And then for pos all the guarded accesses are compared to see if data races might exist 29 | 30 | func Analysis(pkg *ssa.Package, accesses []*domain.GuardedAccess) ([][]*domain.GuardedAccess, error) { 31 | config := &pointer.Config{ 32 | Mains: []*ssa.Package{pkg}, 33 | } 34 | 35 | positionsToGuardAccesses := map[token.Pos][]*domain.GuardedAccess{} 36 | for _, guardedAccess := range accesses { 37 | if guardedAccess.Pos.IsValid() && pointer.CanPoint(guardedAccess.Value.Type()) { 38 | config.AddQuery(guardedAccess.Value) 39 | // Multiple instructions for the same variable for example write and multiple reads 40 | positionsToGuardAccesses[guardedAccess.Value.Pos()] = append(positionsToGuardAccesses[guardedAccess.Value.Pos()], guardedAccess) 41 | } 42 | } 43 | 44 | result, err := pointer.Analyze(config) 45 | if err != nil { 46 | return nil, err // internal error in pointer analysis 47 | } 48 | 49 | // Join instructions of variables that may point to each other. 50 | for v, l := range result.Queries { 51 | for _, label := range l.PointsTo().Labels() { 52 | allocPos := label.Value().Pos() 53 | queryPos := v.Pos() 54 | if allocPos == queryPos { 55 | continue 56 | } 57 | positionsToGuardAccesses[allocPos] = append(positionsToGuardAccesses[allocPos], positionsToGuardAccesses[queryPos]...) 58 | } 59 | } 60 | conflictingGA := make([][]*domain.GuardedAccess, 0) 61 | for _, guardedAccesses := range positionsToGuardAccesses { 62 | for _, guardedAccessA := range guardedAccesses { 63 | for _, guardedAccessB := range guardedAccesses { 64 | if guardedAccessA.IsConflicting(guardedAccessB) { 65 | conflictingGA = append(conflictingGA, []*domain.GuardedAccess{guardedAccessA, guardedAccessB}) 66 | } 67 | } 68 | } 69 | } 70 | return conflictingGA, nil 71 | } 72 | 73 | func FilterDuplicates(conflictingGAs [][]*domain.GuardedAccess) [][]*domain.GuardedAccess { 74 | foundDataRaces := utils.NewDoubleKeyMap() // To avoid reporting on the same pair of positions more then once. Can happen if for the same place we read and then write. 75 | nonDuplicatesGAs := make([][]*domain.GuardedAccess, 0) 76 | for _, conflict := range conflictingGAs { 77 | isExist := foundDataRaces.IsExist(conflict[0].Pos, conflict[1].Pos) 78 | if !isExist { 79 | foundDataRaces.Add(conflict[0].Pos, conflict[1].Pos) 80 | nonDuplicatesGAs = append(nonDuplicatesGAs, conflict) 81 | } 82 | } 83 | return nonDuplicatesGAs 84 | } 85 | -------------------------------------------------------------------------------- /ssaPureUtils/General.go: -------------------------------------------------------------------------------- 1 | package ssaPureUtils 2 | 3 | import ( 4 | "go/token" 5 | "golang.org/x/tools/go/ssa" 6 | ) 7 | 8 | 9 | func GetMutexPos(value ssa.Value) token.Pos { 10 | val, ok := GetField(value) 11 | if !ok { 12 | return value.Pos() 13 | } 14 | obj := GetUnderlyingObjectFromField(val) 15 | return obj.Pos() 16 | } -------------------------------------------------------------------------------- /ssaPureUtils/Locks.go: -------------------------------------------------------------------------------- 1 | package ssaPureUtils 2 | 3 | import ( 4 | "github.com/amit-davidson/Chronos/utils" 5 | "golang.org/x/tools/go/ssa" 6 | ) 7 | 8 | func IsLock(call *ssa.Function) bool { 9 | return utils.IsCallTo(call, "(*sync.Mutex).Lock") 10 | } 11 | 12 | 13 | func IsUnlock(call *ssa.Function) bool { 14 | return utils.IsCallTo(call, "(*sync.Mutex).Unlock") 15 | } 16 | -------------------------------------------------------------------------------- /ssaPureUtils/Structs.go: -------------------------------------------------------------------------------- 1 | package ssaPureUtils 2 | 3 | import ( 4 | "go/types" 5 | "golang.org/x/tools/go/ssa" 6 | ) 7 | 8 | func GetField(value ssa.Value) (*ssa.FieldAddr, bool) { 9 | fieldAddr, ok := value.(*ssa.FieldAddr) 10 | return fieldAddr, ok 11 | } 12 | 13 | func GetUnderlyingObjectFromField(fieldAddr *ssa.FieldAddr) *types.Var { 14 | return fieldAddr.X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct).Field(fieldAddr.Field) 15 | } 16 | 17 | func FilterStructs(valueA, valueB ssa.Value) bool { 18 | fieldAddrA, okA := GetField(valueA) 19 | fieldAddrB, okB := GetField(valueB) 20 | 21 | isBothField := okA && okB 22 | if isBothField { 23 | fieldA := GetUnderlyingObjectFromField(fieldAddrA) 24 | fieldB := GetUnderlyingObjectFromField(fieldAddrB) 25 | if fieldA != fieldB { // If same struct but different fields 26 | return true 27 | } 28 | } 29 | 30 | return false 31 | } 32 | -------------------------------------------------------------------------------- /ssaUtils/CFG.go: -------------------------------------------------------------------------------- 1 | package ssaUtils 2 | 3 | import ( 4 | "github.com/amit-davidson/Chronos/domain" 5 | "github.com/amit-davidson/Chronos/utils/stacks" 6 | "golang.org/x/tools/go/ssa" 7 | ) 8 | 9 | type CFG struct { 10 | visitedBlocksStack *stacks.BlockMap 11 | 12 | ComputedBlocks map[int]*domain.BlockState 13 | ComputedDeferBlocks map[int]*domain.BlockState 14 | } 15 | 16 | func newCFG() *CFG { 17 | return &CFG{ 18 | visitedBlocksStack: stacks.NewBlockMap(), 19 | ComputedBlocks: make(map[int]*domain.BlockState), 20 | ComputedDeferBlocks: make(map[int]*domain.BlockState), 21 | } 22 | } 23 | 24 | // CalculateFunctionState works by traversing the tree in a DFS way, similar to the flow of the function when it'll run. 25 | // It calculates the state of the regular flow of block first, then adds the state of any succeeding blocks in the tree, 26 | // and finally the block's defer state if exist. 27 | // The function uses two way to aggregate the states between blocks. If the blocks are adjacent (siblings) to each 28 | // other, (resulted from a branch) then a merge mechanism is used. If one block is below the other, then an append is 29 | // performed. 30 | func (cfg *CFG) CalculateFunctionState(context *domain.Context, block *ssa.BasicBlock) *domain.BlockState { 31 | cfg.visitedBlocksStack.Add(block) 32 | defer cfg.visitedBlocksStack.Remove(block) 33 | cfg.calculateBlockState(context, block) 34 | 35 | // Regular flow 36 | blockState := cfg.ComputedBlocks[block.Index] 37 | 38 | // recursion 39 | var branchState *domain.BlockState 40 | for _, nextBlock := range block.Succs { 41 | // if it's a cycle we skip it 42 | if cfg.visitedBlocksStack.Contains(nextBlock.Index) { 43 | continue 44 | } 45 | 46 | retBlockState := cfg.CalculateFunctionState(context, nextBlock) 47 | if branchState == nil { 48 | branchState = retBlockState.Copy() 49 | } else { 50 | branchState.MergeSiblingBlock(retBlockState) 51 | } 52 | } 53 | 54 | if branchState != nil { 55 | blockState.MergeChildBlock(branchState) 56 | } 57 | 58 | // Defer 59 | if deferState, ok := cfg.ComputedDeferBlocks[block.Index]; ok { 60 | blockState.MergeChildBlock(deferState) 61 | } 62 | return blockState 63 | } 64 | 65 | func (cfg *CFG) calculateBlockState(context *domain.Context, block *ssa.BasicBlock) { 66 | if _, ok := cfg.ComputedBlocks[block.Index]; !ok { 67 | cfg.ComputedBlocks[block.Index] = GetBlockSummary(context, block) 68 | deferedFunctions := cfg.ComputedBlocks[block.Index].DeferredFunctions 69 | if deferedFunctions.Len() > 0 { 70 | cfg.ComputedDeferBlocks[block.Index] = cfg.runDefers(context, deferedFunctions) 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /ssaUtils/Functions.go: -------------------------------------------------------------------------------- 1 | package ssaUtils 2 | 3 | import ( 4 | "github.com/amit-davidson/Chronos/domain" 5 | "github.com/amit-davidson/Chronos/ssaPureUtils" 6 | "github.com/amit-davidson/Chronos/utils/stacks" 7 | "go/token" 8 | "go/types" 9 | "golang.org/x/tools/go/ssa" 10 | "strings" 11 | ) 12 | 13 | var functionsCache = make(map[*types.Signature]*domain.FunctionState) 14 | 15 | func HandleCallCommon(context *domain.Context, callCommon *ssa.CallCommon, pos token.Pos) *domain.BlockState { 16 | funcState := domain.GetEmptyBlockState() 17 | 18 | // if we already visited this path, it means we're (probably) in a recursion so we return to avoid infinite loop 19 | if context.StackTrace.Contains(int(pos)) { 20 | return funcState 21 | } 22 | 23 | context.StackTrace.Push(int(pos)) 24 | defer context.StackTrace.Pop() 25 | 26 | if callCommon.IsInvoke() { 27 | impls := GetMethodImplementations(callCommon.Value.Type().Underlying(), callCommon.Method) 28 | if len(impls) > 0 { 29 | funcState = HandleFunction(context, impls[0]) 30 | for _, impl := range impls[1:] { 31 | funcstateRet := HandleFunction(context, impl) 32 | funcState.MergeSiblingBlock(funcstateRet) 33 | } 34 | } 35 | return funcState 36 | } 37 | 38 | switch call := callCommon.Value.(type) { 39 | case *ssa.Builtin: 40 | HandleBuiltin(funcState, context, callCommon) 41 | return funcState 42 | case *ssa.MakeClosure: 43 | fn := callCommon.Value.(*ssa.MakeClosure).Fn.(*ssa.Function) 44 | funcStateRet := HandleFunction(context, fn) 45 | return funcStateRet 46 | case *ssa.Function: 47 | if ssaPureUtils.IsLock(call) { 48 | AddLock(funcState, callCommon, false) 49 | return funcState 50 | } 51 | if ssaPureUtils.IsUnlock(call) { 52 | AddLock(funcState, callCommon, true) 53 | return funcState 54 | } 55 | 56 | var blockStateRet *domain.BlockState 57 | sig := callCommon.Signature() 58 | if cachedFunctionState, ok := functionsCache[sig]; ok { 59 | copiedState := cachedFunctionState.Copy() // Copy to avoid override cached item 60 | copiedState.AddContextToFunction(context) 61 | blockStateRet = domain.CreateBlockState(copiedState.GuardedAccesses, copiedState.Lockset, stacks.NewCallCommonStack()) 62 | } else { 63 | blockStateRet = HandleFunction(context, call) 64 | fs := domain.CreateFunctionState(blockStateRet.GuardedAccesses, blockStateRet.Lockset) 65 | fs.RemoveContextFromFunction() 66 | functionsCache[sig] = fs 67 | } 68 | return blockStateRet 69 | 70 | case ssa.Instruction: 71 | HandleInstruction(funcState, context, call) 72 | return funcState 73 | } 74 | return funcState 75 | } 76 | 77 | func HandleBuiltin(functionState *domain.BlockState, context *domain.Context, call *ssa.CallCommon) { 78 | callCommon := call.Value.(*ssa.Builtin) 79 | args := call.Args 80 | switch name := callCommon.Name(); name { 81 | case "delete": 82 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, domain.AddGuardedAccess(call.Pos(), args[0], domain.GuardAccessWrite, functionState.Lockset, context)) 83 | case "cap", "len": 84 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, domain.AddGuardedAccess(call.Pos(), args[0], domain.GuardAccessRead, functionState.Lockset, context)) 85 | case "append": 86 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, domain.AddGuardedAccess(call.Pos(), args[1], domain.GuardAccessRead, functionState.Lockset, context)) 87 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, domain.AddGuardedAccess(call.Pos(), args[0], domain.GuardAccessWrite, functionState.Lockset, context)) 88 | case "copy": 89 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, domain.AddGuardedAccess(call.Pos(), args[0], domain.GuardAccessRead, functionState.Lockset, context)) 90 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, domain.AddGuardedAccess(call.Pos(), args[1], domain.GuardAccessWrite, functionState.Lockset, context)) 91 | } 92 | } 93 | 94 | func HandleInstruction(functionState *domain.BlockState, context *domain.Context, ins ssa.Instruction) { 95 | switch call := ins.(type) { 96 | case *ssa.UnOp: 97 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.X, domain.GuardAccessRead, functionState.Lockset, context) 98 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess) 99 | case *ssa.Field: 100 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call, domain.GuardAccessRead, functionState.Lockset, context) 101 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess) 102 | case *ssa.FieldAddr: 103 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call, domain.GuardAccessRead, functionState.Lockset, context) 104 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess) 105 | case *ssa.Index: 106 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.X, domain.GuardAccessRead, functionState.Lockset, context) 107 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess) 108 | case *ssa.IndexAddr: 109 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.X, domain.GuardAccessRead, functionState.Lockset, context) 110 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess) 111 | case *ssa.Lookup: 112 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.X, domain.GuardAccessRead, functionState.Lockset, context) 113 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess) 114 | case *ssa.Panic: 115 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.X, domain.GuardAccessRead, functionState.Lockset, context) 116 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess) 117 | case *ssa.Range: 118 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.X, domain.GuardAccessRead, functionState.Lockset, context) 119 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess) 120 | case *ssa.TypeAssert: 121 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.X, domain.GuardAccessRead, functionState.Lockset, context) 122 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess) 123 | case *ssa.BinOp: 124 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.X, domain.GuardAccessRead, functionState.Lockset, context) 125 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess) 126 | guardedAccess = domain.AddGuardedAccess(call.Pos(), call.Y, domain.GuardAccessRead, functionState.Lockset, context) 127 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess) 128 | case *ssa.If: 129 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.Cond, domain.GuardAccessRead, functionState.Lockset, context) 130 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess) 131 | case *ssa.MapUpdate: 132 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.Map, domain.GuardAccessWrite, functionState.Lockset, context) 133 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess) 134 | guardedAccess = domain.AddGuardedAccess(call.Pos(), call.Value, domain.GuardAccessRead, functionState.Lockset, context) 135 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess) 136 | case *ssa.Store: 137 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.Val, domain.GuardAccessRead, functionState.Lockset, context) 138 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess) 139 | guardedAccess = domain.AddGuardedAccess(call.Pos(), call.Addr, domain.GuardAccessWrite, functionState.Lockset, context) 140 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess) 141 | case *ssa.Return: 142 | for _, retValue := range call.Results { 143 | guardedAccess := domain.AddGuardedAccess(call.Pos(), retValue, domain.GuardAccessRead, functionState.Lockset, context) 144 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess) 145 | } 146 | } 147 | } 148 | 149 | func GetBlockSummary(context *domain.Context, block *ssa.BasicBlock) *domain.BlockState { 150 | funcState := domain.GetEmptyBlockState() 151 | for _, ins := range block.Instrs { 152 | switch call := ins.(type) { 153 | case *ssa.Call: 154 | callCommon := call.Common() 155 | funcStateRet := HandleCallCommon(context, callCommon, callCommon.Pos()) 156 | funcState.AddFunctionCallState(funcStateRet, true) 157 | case *ssa.Go: 158 | callCommon := call.Common() 159 | newState := domain.NewGoroutineExecutionState(context) 160 | funcStateRet := HandleCallCommon(newState, callCommon, callCommon.Pos()) 161 | funcState.AddFunctionCallState(funcStateRet, false) 162 | case *ssa.Defer: 163 | callCommon := call.Common() 164 | funcState.DeferredFunctions.Push(callCommon) 165 | default: 166 | HandleInstruction(funcState, context, ins) 167 | } 168 | } 169 | return funcState 170 | } 171 | 172 | func (cfg *CFG) runDefers(context *domain.Context, defers *stacks.CallCommonStack) *domain.BlockState { 173 | calculatedState := domain.GetEmptyBlockState() 174 | defersItems := defers.GetItems() 175 | for i := len(defersItems) - 1; i >= 0; i-- { 176 | deferFunction := defersItems[i] 177 | if deferFunction == nil { 178 | break 179 | } 180 | retState := HandleCallCommon(context, deferFunction, deferFunction.Pos()) 181 | calculatedState.MergeChildBlock(retState) 182 | } 183 | return calculatedState 184 | 185 | } 186 | 187 | func HandleFunction(context *domain.Context, fn *ssa.Function) *domain.BlockState { 188 | funcState := domain.GetEmptyBlockState() 189 | if fn.Pkg == nil { 190 | return funcState 191 | } 192 | pkgName := fn.Pkg.Pkg.Path() // Used to guard against entering standard library packages 193 | if !strings.Contains(pkgName, GlobalModuleName) { 194 | return funcState 195 | } 196 | 197 | // regular 198 | if fn.Blocks == nil { // External function 199 | return funcState 200 | } 201 | cfg := newCFG() 202 | calculatedState := cfg.CalculateFunctionState(context, fn.Blocks[0]) 203 | return calculatedState 204 | } 205 | -------------------------------------------------------------------------------- /ssaUtils/Functions_test.go: -------------------------------------------------------------------------------- 1 | package ssaUtils 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/amit-davidson/Chronos/domain" 7 | "github.com/amit-davidson/Chronos/pointerAnalysis" 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | "golang.org/x/tools/go/ssa" 11 | ) 12 | 13 | func Test_HandleFunction_DeferredLockAndUnlockIfBranch(t *testing.T) { 14 | f, _ := LoadMain(t, "./testdata/Functions/Defer/DeferredLockAndUnlockIfBranch/prog1.go") 15 | ctx := domain.NewEmptyContext() 16 | state := HandleFunction(ctx, f) 17 | assert.Len(t, state.Lockset.Locks, 0) 18 | assert.Len(t, state.Lockset.Unlocks, 1) 19 | 20 | foundGA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 21 | if !IsGARead(ga) { 22 | return false 23 | } 24 | val, ok := ga.Value.(*ssa.Const) 25 | if !ok { 26 | return false 27 | } 28 | if val.Int64() != 5 { 29 | return false 30 | } 31 | return true 32 | }) 33 | assert.Len(t, foundGA.Lockset.Locks, 0) 34 | 35 | foundGA = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 36 | if !IsGARead(ga) { 37 | return false 38 | } 39 | val, ok := ga.Value.(*ssa.Const) 40 | if !ok { 41 | return false 42 | } 43 | if val.Int64() != 6 { 44 | return false 45 | } 46 | return true 47 | }) 48 | assert.Len(t, foundGA.Lockset.Locks, 0) 49 | } 50 | 51 | func Test_HandleFunction_NestedDeferWithLockAndUnlock(t *testing.T) { 52 | f, _ := LoadMain(t, "./testdata/Functions/Defer/NestedDeferWithLockAndUnlock/prog1.go") 53 | ctx := domain.NewEmptyContext() 54 | state := HandleFunction(ctx, f) 55 | assert.Len(t, state.Lockset.Locks, 1) 56 | assert.Len(t, state.Lockset.Unlocks, 0) 57 | 58 | foundGA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 59 | if !IsGARead(ga) { 60 | return false 61 | } 62 | val, ok := ga.Value.(*ssa.Const) 63 | if !ok { 64 | return false 65 | } 66 | if val.Int64() != 6 { 67 | return false 68 | } 69 | return true 70 | }) 71 | assert.Len(t, foundGA.Lockset.Locks, 1) 72 | } 73 | 74 | func Test_HandleFunction_NestedDeferWithLockAndUnlockAndGoroutine(t *testing.T) { 75 | f, _ := LoadMain(t, "./testdata/Functions/Defer/NestedDeferWithLockAndUnlockAndGoroutine/prog1.go") 76 | ctx := domain.NewEmptyContext() 77 | state := HandleFunction(ctx, f) 78 | assert.Len(t, state.Lockset.Locks, 0) 79 | assert.Len(t, state.Lockset.Unlocks, 1) 80 | 81 | foundGA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 82 | if !IsGARead(ga) { 83 | return false 84 | } 85 | val, ok := ga.Value.(*ssa.Const) 86 | if !ok { 87 | return false 88 | } 89 | if val.Int64() != 6 { 90 | return false 91 | } 92 | return true 93 | }) 94 | assert.Len(t, foundGA.Lockset.Locks, 1) 95 | 96 | foundGA = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 97 | if !IsGARead(ga) { 98 | return false 99 | } 100 | val, ok := ga.Value.(*ssa.Const) 101 | if !ok { 102 | return false 103 | } 104 | if val.Int64() != 7 { 105 | return false 106 | } 107 | return true 108 | }) 109 | assert.Len(t, foundGA.Lockset.Locks, 1) 110 | } 111 | 112 | func Test_HandleFunction_ForLoopLockInsideLoop(t *testing.T) { 113 | f, _ := LoadMain(t, "./testdata/Functions/ForLoops/ForLoopLockInsideLoop/prog1.go") 114 | ctx := domain.NewEmptyContext() 115 | state := HandleFunction(ctx, f) 116 | assert.Len(t, state.Lockset.Locks, 0) 117 | 118 | foundGA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 119 | if !IsGARead(ga) { 120 | return false 121 | } 122 | val, ok := ga.Value.(*ssa.Const) 123 | if !ok { 124 | return false 125 | } 126 | if GetConstString(val) != "b" { 127 | return false 128 | } 129 | return true 130 | }) 131 | assert.Len(t, foundGA.Lockset.Locks, 0) 132 | assert.Len(t, foundGA.Lockset.Unlocks, 0) 133 | 134 | foundGA = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 135 | if !IsGARead(ga) { 136 | return false 137 | } 138 | val, ok := ga.Value.(*ssa.Const) 139 | if !ok { 140 | return false 141 | } 142 | if GetConstString(val) != "c" { 143 | return false 144 | } 145 | return true 146 | }) 147 | assert.Len(t, foundGA.Lockset.Locks, 1) 148 | assert.Len(t, foundGA.Lockset.Unlocks, 0) 149 | } 150 | 151 | func Test_HandleFunction_ForLoopLockOutsideLoop(t *testing.T) { 152 | f, _ := LoadMain(t, "./testdata/Functions/ForLoops/ForLoopLockOutsideLoop/prog1.go") 153 | ctx := domain.NewEmptyContext() 154 | state := HandleFunction(ctx, f) 155 | assert.Len(t, state.Lockset.Locks, 1) 156 | 157 | foundGA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 158 | if !IsGARead(ga) { 159 | return false 160 | } 161 | val, ok := ga.Value.(*ssa.Const) 162 | if !ok { 163 | return false 164 | } 165 | if GetConstString(val) != "b" { 166 | return false 167 | } 168 | return true 169 | }) 170 | assert.Len(t, foundGA.Lockset.Locks, 0) 171 | assert.Len(t, foundGA.Lockset.Unlocks, 0) 172 | 173 | foundGA = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 174 | if !IsGARead(ga) { 175 | return false 176 | } 177 | val, ok := ga.Value.(*ssa.Const) 178 | if !ok { 179 | return false 180 | } 181 | if GetConstString(val) != "c" { 182 | return false 183 | } 184 | return true 185 | }) 186 | assert.Len(t, foundGA.Lockset.Locks, 1) 187 | assert.Len(t, foundGA.Lockset.Unlocks, 0) 188 | } 189 | 190 | func Test_HandleFunction_NestedForLoopWithRace(t *testing.T) { 191 | f, _ := LoadMain(t, "./testdata/Functions/ForLoops/NestedForLoopWithRace/prog1.go") 192 | ctx := domain.NewEmptyContext() 193 | state := HandleFunction(ctx, f) 194 | assert.Len(t, state.Lockset.Locks, 0) 195 | assert.Len(t, state.Lockset.Unlocks, 0) 196 | 197 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 198 | if !IsGARead(ga) { 199 | return false 200 | } 201 | val, ok := ga.Value.(*ssa.Const) 202 | if !ok { 203 | return false 204 | } 205 | if GetConstString(val) != "a" { 206 | return false 207 | } 208 | return true 209 | }) 210 | stateA := ga.State 211 | assert.Len(t, ga.Lockset.Locks, 0) 212 | assert.Len(t, ga.Lockset.Unlocks, 0) 213 | 214 | ga = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 215 | if !IsGARead(ga) { 216 | return false 217 | } 218 | val, ok := ga.Value.(*ssa.Const) 219 | if !ok { 220 | return false 221 | } 222 | return GetConstString(val) == "b" 223 | }) 224 | stateB := ga.State 225 | assert.Len(t, ga.Lockset.Locks, 0) 226 | assert.Len(t, ga.Lockset.Unlocks, 0) 227 | assert.True(t, stateA.MayConcurrent(stateB)) 228 | 229 | } 230 | 231 | func Test_HandleFunction_WhileLoop(t *testing.T) { 232 | f, _ := LoadMain(t, "./testdata/Functions/ForLoops/WhileLoop/prog1.go") 233 | ctx := domain.NewEmptyContext() 234 | state := HandleFunction(ctx, f) 235 | assert.Len(t, state.Lockset.Locks, 0) 236 | assert.Len(t, state.Lockset.Unlocks, 0) 237 | 238 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 239 | if !IsGARead(ga) { 240 | return false 241 | } 242 | _, ok := ga.Value.(*ssa.Alloc) 243 | if !ok { 244 | return false 245 | } 246 | pName := ga.Value.Parent().Name() 247 | return pName != "main" 248 | }) 249 | stateA := ga.State 250 | assert.Len(t, ga.Lockset.Locks, 0) 251 | assert.Len(t, ga.Lockset.Unlocks, 0) 252 | 253 | ga = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 254 | if !IsGARead(ga) { 255 | return false 256 | } 257 | val, ok := ga.Value.(*ssa.FreeVar) 258 | if !ok { 259 | return false 260 | } 261 | return val.Name() == "x" 262 | }) 263 | stateB := ga.State 264 | assert.Len(t, ga.Lockset.Locks, 0) 265 | assert.Len(t, ga.Lockset.Unlocks, 0) 266 | assert.True(t, stateA.MayConcurrent(stateB)) 267 | 268 | } 269 | 270 | func Test_HandleFunction_WhileLoopWithoutHeader(t *testing.T) { 271 | f, _ := LoadMain(t, "./testdata/Functions/ForLoops/WhileLoopWithoutHeader/prog1.go") 272 | ctx := domain.NewEmptyContext() 273 | state := HandleFunction(ctx, f) 274 | assert.Len(t, state.Lockset.Locks, 0) 275 | assert.Len(t, state.Lockset.Unlocks, 0) 276 | 277 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 278 | if !IsGARead(ga) { 279 | return false 280 | } 281 | _, ok := ga.Value.(*ssa.Alloc) 282 | if !ok { 283 | return false 284 | } 285 | pName := ga.Value.Parent().Name() 286 | return pName != "main" 287 | }) 288 | stateA := ga.State 289 | assert.Len(t, ga.Lockset.Locks, 1) 290 | assert.Len(t, ga.Lockset.Unlocks, 0) 291 | 292 | ga = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 293 | if !IsGARead(ga) { 294 | return false 295 | } 296 | val, ok := ga.Value.(*ssa.FreeVar) 297 | if !ok { 298 | return false 299 | } 300 | if val.Name() != "x" { 301 | return false 302 | } 303 | return true 304 | }) 305 | stateB := ga.State 306 | assert.Len(t, ga.Lockset.Locks, 0) 307 | assert.Len(t, ga.Lockset.Unlocks, 0) 308 | assert.True(t, stateA.MayConcurrent(stateB)) 309 | } 310 | 311 | func Test_HandleFunction_DataRaceIceCreamMaker(t *testing.T) { 312 | f, _ := LoadMain(t, "./testdata/Functions/Interfaces/DataRaceIceCreamMaker/prog1.go") 313 | ctx := domain.NewEmptyContext() 314 | state := HandleFunction(ctx, f) 315 | assert.Len(t, state.Lockset.Locks, 0) 316 | assert.Len(t, state.Lockset.Unlocks, 0) 317 | 318 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 319 | if !IsGARead(ga) { 320 | return false 321 | } 322 | val, ok := ga.Value.(*ssa.Const) 323 | if !ok { 324 | return false 325 | } 326 | if GetConstString(val) != "Ben" { 327 | return false 328 | } 329 | return true 330 | }) 331 | stateA := ga.State 332 | assert.Len(t, ga.Lockset.Locks, 0) 333 | assert.Len(t, ga.Lockset.Unlocks, 0) 334 | 335 | ga = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 336 | if !IsGARead(ga) { 337 | return false 338 | } 339 | val, ok := ga.Value.(*ssa.Const) 340 | if !ok { 341 | return false 342 | } 343 | if GetConstString(val) != "1" { 344 | return false 345 | } 346 | return true 347 | }) 348 | stateB := ga.State 349 | assert.Len(t, ga.Lockset.Locks, 0) 350 | assert.Len(t, ga.Lockset.Unlocks, 0) 351 | assert.True(t, stateA.MayConcurrent(stateB)) 352 | } 353 | 354 | func Test_HandleFunction_InterfaceWithLock(t *testing.T) { 355 | f, _ := LoadMain(t, "./testdata/Functions/Interfaces/InterfaceWithLock/prog1.go") 356 | ctx := domain.NewEmptyContext() 357 | state := HandleFunction(ctx, f) 358 | assert.Len(t, state.Lockset.Locks, 1) 359 | assert.Len(t, state.Lockset.Unlocks, 0) 360 | 361 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 362 | if !IsGARead(ga) { 363 | return false 364 | } 365 | val, ok := ga.Value.(*ssa.Const) 366 | if !ok { 367 | return false 368 | } 369 | if GetConstString(val) != "Ben" { 370 | return false 371 | } 372 | return true 373 | }) 374 | assert.Len(t, ga.Lockset.Locks, 1) 375 | assert.Len(t, ga.Lockset.Unlocks, 0) 376 | 377 | ga = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 378 | if !IsGARead(ga) { 379 | return false 380 | } 381 | val, ok := ga.Value.(*ssa.Const) 382 | if !ok { 383 | return false 384 | } 385 | if GetConstString(val) != "Jerry" { 386 | return false 387 | } 388 | return true 389 | }) 390 | assert.Len(t, ga.Lockset.Locks, 1) 391 | assert.Len(t, ga.Lockset.Unlocks, 0) 392 | 393 | ga = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 394 | if !IsGARead(ga) { 395 | return false 396 | } 397 | val, ok := ga.Value.(*ssa.Const) 398 | if !ok { 399 | return false 400 | } 401 | if GetConstString(val) != "1" { 402 | return false 403 | } 404 | return true 405 | }) 406 | assert.Len(t, ga.Lockset.Locks, 1) 407 | assert.Len(t, ga.Lockset.Unlocks, 0) 408 | } 409 | 410 | func Test_HandleFunction_NestedInterface(t *testing.T) { 411 | f, _ := LoadMain(t, "./testdata/Functions/Interfaces/NestedInterface/prog1.go") 412 | ctx := domain.NewEmptyContext() 413 | state := HandleFunction(ctx, f) 414 | assert.Len(t, state.Lockset.Locks, 0) 415 | assert.Len(t, state.Lockset.Unlocks, 0) 416 | 417 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 418 | if !IsGARead(ga) { 419 | return false 420 | } 421 | val, ok := ga.Value.(*ssa.Const) 422 | if !ok { 423 | return false 424 | } 425 | if GetConstString(val) != "Jerry" { 426 | return false 427 | } 428 | return true 429 | }) 430 | assert.Len(t, ga.Lockset.Locks, 0) 431 | assert.Len(t, ga.Lockset.Unlocks, 0) 432 | } 433 | 434 | func Test_HandleFunction_Lock(t *testing.T) { 435 | f, _ := LoadMain(t, "./testdata/Functions/LocksAndUnlocks/Lock/prog1.go") 436 | ctx := domain.NewEmptyContext() 437 | state := HandleFunction(ctx, f) 438 | assert.Len(t, state.Lockset.Locks, 1) 439 | assert.Len(t, state.Lockset.Unlocks, 0) 440 | 441 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 442 | if !IsGARead(ga) { 443 | return false 444 | } 445 | val, ok := ga.Value.(*ssa.Const) 446 | if !ok { 447 | return false 448 | } 449 | if val.Int64() != 5 { 450 | return false 451 | } 452 | return true 453 | }) 454 | assert.Len(t, ga.Lockset.Locks, 1) 455 | assert.Len(t, ga.Lockset.Unlocks, 0) 456 | } 457 | 458 | func Test_HandleFunction_LockAndUnlock(t *testing.T) { 459 | f, _ := LoadMain(t, "./testdata/Functions/LocksAndUnlocks/LockAndUnlock/prog1.go") 460 | ctx := domain.NewEmptyContext() 461 | state := HandleFunction(ctx, f) 462 | assert.Len(t, state.Lockset.Locks, 0) 463 | assert.Len(t, state.Lockset.Unlocks, 1) 464 | 465 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 466 | if !IsGARead(ga) { 467 | return false 468 | } 469 | val, ok := ga.Value.(*ssa.Const) 470 | if !ok { 471 | return false 472 | } 473 | if val.Int64() != 5 { 474 | return false 475 | } 476 | return true 477 | }) 478 | assert.Len(t, ga.Lockset.Locks, 0) 479 | assert.Len(t, ga.Lockset.Unlocks, 1) 480 | } 481 | 482 | func Test_HandleFunction_LockAndUnlockIfBranch(t *testing.T) { 483 | f, _ := LoadMain(t, "./testdata/Functions/LocksAndUnlocks/LockAndUnlockIfBranch/prog1.go") 484 | ctx := domain.NewEmptyContext() 485 | state := HandleFunction(ctx, f) 486 | assert.Len(t, state.Lockset.Locks, 0) 487 | assert.Len(t, state.Lockset.Unlocks, 1) 488 | 489 | foundGA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 490 | if !IsGARead(ga) { 491 | return false 492 | } 493 | val, ok := ga.Value.(*ssa.Const) 494 | if !ok { 495 | return false 496 | } 497 | if val.Int64() != 5 { 498 | return false 499 | } 500 | return true 501 | }) 502 | assert.Len(t, foundGA.Lockset.Locks, 0) 503 | 504 | foundGA = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 505 | if !IsGARead(ga) { 506 | return false 507 | } 508 | val, ok := ga.Value.(*ssa.Const) 509 | if !ok { 510 | return false 511 | } 512 | if val.Int64() != 6 { 513 | return false 514 | } 515 | return true 516 | }) 517 | assert.Len(t, foundGA.Lockset.Locks, 0) 518 | } 519 | 520 | func Test_HandleFunction_LockInBothBranches(t *testing.T) { 521 | f, _ := LoadMain(t, "./testdata/Functions/LocksAndUnlocks/LockInBothBranches/prog1.go") 522 | ctx := domain.NewEmptyContext() 523 | state := HandleFunction(ctx, f) 524 | assert.Len(t, state.Lockset.Locks, 1) 525 | assert.Len(t, state.Lockset.Unlocks, 0) 526 | 527 | foundGA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 528 | if !IsGARead(ga) { 529 | return false 530 | } 531 | val, ok := ga.Value.(*ssa.Const) 532 | if !ok { 533 | return false 534 | } 535 | if val.Int64() != 5 { 536 | return false 537 | } 538 | return true 539 | }) 540 | assert.Len(t, foundGA.Lockset.Locks, 1) 541 | } 542 | 543 | func Test_HandleFunction_LockInsideGoroutine(t *testing.T) { 544 | f, _ := LoadMain(t, "./testdata/Functions/LocksAndUnlocks/LockInsideGoroutine/prog1.go") 545 | ctx := domain.NewEmptyContext() 546 | state := HandleFunction(ctx, f) 547 | assert.Len(t, state.Lockset.Locks, 0) 548 | assert.Len(t, state.Lockset.Unlocks, 0) 549 | 550 | foundGA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 551 | if !IsGARead(ga) { 552 | return false 553 | } 554 | _, ok := ga.Value.(*ssa.MakeInterface) 555 | if !ok { 556 | return false 557 | } 558 | pName := ga.Value.Parent().Name() 559 | return pName != "main" 560 | }) 561 | assert.Len(t, foundGA.Lockset.Locks, 1) 562 | 563 | foundGA = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 564 | if !IsGARead(ga) { 565 | return false 566 | } 567 | _, ok := ga.Value.(*ssa.MakeInterface) 568 | if !ok { 569 | return false 570 | } 571 | pName := ga.Value.Parent().Name() 572 | return pName == "main" 573 | 574 | }) 575 | assert.Len(t, foundGA.Lockset.Locks, 0) 576 | } 577 | 578 | func Test_HandleFunction_MultipleLocksNoRace(t *testing.T) { 579 | f, _ := LoadMain(t, "./testdata/Functions/LocksAndUnlocks/MultipleLocksNoRace/prog1.go") 580 | ctx := domain.NewEmptyContext() 581 | state := HandleFunction(ctx, f) 582 | assert.Len(t, state.Lockset.Locks, 0) 583 | assert.Len(t, state.Lockset.Unlocks, 0) 584 | 585 | GA1 := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 586 | if !IsGARead(ga) { 587 | return false 588 | } 589 | val, ok := ga.Value.(*ssa.Const) 590 | if !ok { 591 | return false 592 | } 593 | if val.Int64() != 1 { 594 | return false 595 | } 596 | return true 597 | }) 598 | assert.Len(t, GA1.Lockset.Locks, 1) 599 | 600 | GA2 := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 601 | if !IsGARead(ga) { 602 | return false 603 | } 604 | val, ok := ga.Value.(*ssa.Const) 605 | if !ok { 606 | return false 607 | } 608 | if val.Int64() != 2 { 609 | return false 610 | } 611 | return true 612 | }) 613 | 614 | assert.Len(t, GA2.Lockset.Locks, 1) 615 | assert.False(t, GA1.IsConflicting(GA2)) 616 | } 617 | 618 | func Test_HandleFunction_NestedConditionWithLockInAllBranches(t *testing.T) { 619 | f, _ := LoadMain(t, "./testdata/Functions/LocksAndUnlocks/NestedConditionWithLockInAllBranches/prog1.go") 620 | ctx := domain.NewEmptyContext() 621 | state := HandleFunction(ctx, f) 622 | assert.Len(t, state.Lockset.Locks, 1) 623 | assert.Len(t, state.Lockset.Unlocks, 0) 624 | 625 | GA1 := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 626 | if !IsGARead(ga) { 627 | return false 628 | } 629 | _, ok := ga.Value.(*ssa.MakeInterface) 630 | return ok 631 | }) 632 | assert.Len(t, GA1.Lockset.Locks, 1) 633 | } 634 | 635 | func Test_HandleFunction_NestedLockInStruct(t *testing.T) { 636 | f, _ := LoadMain(t, "./testdata/Functions/LocksAndUnlocks/NestedLockInStruct/prog1.go") 637 | ctx := domain.NewEmptyContext() 638 | state := HandleFunction(ctx, f) 639 | assert.Len(t, state.Lockset.Locks, 0) 640 | assert.Len(t, state.Lockset.Unlocks, 1) 641 | 642 | GA1 := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 643 | if !IsGARead(ga) { 644 | return false 645 | } 646 | val, ok := ga.Value.(*ssa.Const) 647 | if !ok { 648 | return false 649 | } 650 | if val.Int64() != 5 { 651 | return false 652 | } 653 | return true 654 | }) 655 | assert.Len(t, GA1.Lockset.Locks, 0) 656 | assert.Len(t, GA1.Lockset.Unlocks, 1) 657 | } 658 | 659 | func Test_HandleFunction_DataRaceGoto(t *testing.T) { 660 | f, pkg := LoadMain(t, "./testdata/Functions/General/DataRaceGoto/prog1.go") 661 | ctx := domain.NewEmptyContext() 662 | state := HandleFunction(ctx, f) 663 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses) 664 | require.NoError(t, err) 665 | gas := FindMultipleGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 666 | if !IsGAWrite(ga) { 667 | return false 668 | } 669 | val, ok := ga.Value.(*ssa.Global) 670 | if !ok { 671 | return false 672 | } 673 | if GetGlobalString(val) != "a" { 674 | return false 675 | } 676 | return true 677 | }, 2) 678 | 679 | found := false 680 | for _, ca := range conflictingAccesses { 681 | if EqualDifferentOrder(gas, ca) { 682 | found = true 683 | } 684 | } 685 | assert.True(t, found) 686 | 687 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 688 | if !IsGAWrite(ga) { 689 | return false 690 | } 691 | val, ok := ga.Value.(*ssa.Global) 692 | if !ok { 693 | return false 694 | } 695 | if GetGlobalString(val) != "a" { 696 | return false 697 | } 698 | return true 699 | }) 700 | 701 | found = false 702 | for _, ca := range conflictingAccesses { 703 | if EqualDifferentOrder([]*domain.GuardedAccess{gas[1], ga}, ca) { 704 | found = true 705 | } 706 | } 707 | assert.True(t, found) 708 | } 709 | 710 | func Test_HandleFunction_DataRaceMap(t *testing.T) { 711 | f, pkg := LoadMain(t, "./testdata/Functions/General/DataRaceMap/prog1.go") 712 | ctx := domain.NewEmptyContext() 713 | state := HandleFunction(ctx, f) 714 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses) 715 | require.NoError(t, err) 716 | gaA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 717 | if !IsGAWrite(ga) { 718 | return false 719 | } 720 | val, ok := ga.Value.(*ssa.UnOp) 721 | if !ok { 722 | return false 723 | } 724 | X, ok := val.X.(*ssa.FreeVar) 725 | if !ok { 726 | return false 727 | } 728 | if X.Name() != "m" { 729 | return false 730 | } 731 | return true 732 | }) 733 | 734 | gaB := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 735 | if !IsGAWrite(ga) { 736 | return false 737 | } 738 | val, ok := ga.Value.(*ssa.UnOp) 739 | if !ok { 740 | return false 741 | } 742 | X, ok := val.X.(*ssa.Alloc) 743 | if !ok { 744 | return false 745 | } 746 | if X.Comment != "m" { 747 | return false 748 | } 749 | return true 750 | }) 751 | 752 | found := false 753 | for _, ca := range conflictingAccesses { 754 | if EqualDifferentOrder([]*domain.GuardedAccess{gaA, gaB}, ca) { 755 | found = true 756 | } 757 | } 758 | assert.True(t, found) 759 | } 760 | 761 | func Test_HandleFunction_DataRaceNestedSameFunction(t *testing.T) { 762 | f, pkg := LoadMain(t, "./testdata/Functions/General/DataRaceNestedSameFunction/prog1.go") 763 | ctx := domain.NewEmptyContext() 764 | state := HandleFunction(ctx, f) 765 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses) 766 | require.NoError(t, err) 767 | gas := FindMultipleGA(state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 768 | global, ok := ga.Value.(*ssa.Global) 769 | if !ok { 770 | return false 771 | } 772 | if global.Name() != "count" { 773 | return false 774 | } 775 | return true 776 | }) 777 | 778 | assert.Subset(t, gas, conflictingAccesses[0]) 779 | } 780 | 781 | func Test_HandleFunction_DataRaceProperty(t *testing.T) { 782 | f, pkg := LoadMain(t, "./testdata/Functions/General/DataRaceProperty/prog1.go") 783 | ctx := domain.NewEmptyContext() 784 | state := HandleFunction(ctx, f) 785 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses) 786 | require.NoError(t, err) 787 | gaA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 788 | if !IsGAWrite(ga) { 789 | return false 790 | } 791 | field, ok := ga.Value.(*ssa.FieldAddr) 792 | if !ok { 793 | return false 794 | } 795 | if field.X.Name() != "w" { 796 | return false 797 | } 798 | return true 799 | }) 800 | assert.Len(t, gaA.State.Clock, 2) 801 | 802 | gaB := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 803 | if !IsGARead(ga) { 804 | return false 805 | } 806 | field, ok := ga.Value.(*ssa.FieldAddr) 807 | if !ok { 808 | return false 809 | } 810 | _, ok = field.X.(*ssa.UnOp) 811 | return ok 812 | }) 813 | assert.Len(t, gaB.State.Clock, 3) 814 | 815 | found := false 816 | for _, ca := range conflictingAccesses { 817 | if EqualDifferentOrder([]*domain.GuardedAccess{gaA, gaB}, ca) { 818 | found = true 819 | } 820 | } 821 | assert.True(t, found) 822 | } 823 | 824 | func Test_HandleFunction_DataRaceRecursion(t *testing.T) { 825 | f, pkg := LoadMain(t, "./testdata/Functions/General/DataRaceRecursion/prog1.go") 826 | ctx := domain.NewEmptyContext() 827 | state := HandleFunction(ctx, f) 828 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses) 829 | require.NoError(t, err) 830 | gas := FindMultipleGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 831 | if !IsGAWrite(ga) { 832 | return false 833 | } 834 | field, ok := ga.Value.(*ssa.Global) 835 | if !ok { 836 | return false 837 | } 838 | if field.Name() != "a" { 839 | return false 840 | } 841 | return true 842 | }, 3) 843 | 844 | // We check that the recursion is iterated twice. Then we make sure that both cases can conflict with the read at 845 | // the beginning. 846 | found := false 847 | gasToCheck := []*domain.GuardedAccess{gas[0], gas[1]} 848 | for _, ca := range conflictingAccesses { 849 | if EqualDifferentOrder(gasToCheck, ca) { 850 | found = true 851 | } 852 | } 853 | assert.True(t, found) 854 | 855 | found = false 856 | gasToCheck = []*domain.GuardedAccess{gas[0], gas[2]} 857 | for _, ca := range conflictingAccesses { 858 | if EqualDifferentOrder(gasToCheck, ca) { 859 | found = true 860 | } 861 | } 862 | assert.True(t, found) 863 | } 864 | 865 | func Test_HandleFunction_DataRaceShadowedErr(t *testing.T) { 866 | f, pkg := LoadMain(t, "./testdata/Functions/General/DataRaceShadowedErr/prog1.go") 867 | ctx := domain.NewEmptyContext() 868 | state := HandleFunction(ctx, f) 869 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses) 870 | require.NoError(t, err) 871 | assert.Len(t, conflictingAccesses, 6) 872 | filteredAccesses := pointerAnalysis.FilterDuplicates(conflictingAccesses) 873 | assert.Len(t, filteredAccesses, 3) 874 | } 875 | 876 | func Test_HandleFunction_DataRaceWithOnlyAlloc(t *testing.T) { 877 | f, pkg := LoadMain(t, "./testdata/Functions/General/DataRaceWithOnlyAlloc/prog1.go") 878 | ctx := domain.NewEmptyContext() 879 | state := HandleFunction(ctx, f) 880 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses) 881 | require.NoError(t, err) 882 | assert.Len(t, conflictingAccesses, 2) 883 | filteredAccesses := pointerAnalysis.FilterDuplicates(conflictingAccesses) 884 | assert.Len(t, filteredAccesses, 1) 885 | 886 | gaA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 887 | if !IsGAWrite(ga) { 888 | return false 889 | } 890 | _, ok := ga.Value.(*ssa.FreeVar) 891 | return ok 892 | }) 893 | 894 | gaB := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 895 | if !IsGARead(ga) { 896 | return false 897 | } 898 | _, ok := ga.Value.(*ssa.Alloc) 899 | return ok 900 | }) 901 | require.Len(t, filteredAccesses, 1) 902 | assert.True(t, EqualDifferentOrder([]*domain.GuardedAccess{gaA, gaB}, filteredAccesses[0])) 903 | } 904 | 905 | func Test_HandleFunction_DataRaceWithSameFunction(t *testing.T) { 906 | f, pkg := LoadMain(t, "./testdata/Functions/General/DataRaceWithSameFunction/prog1.go") 907 | ctx := domain.NewEmptyContext() 908 | entryCallCommon := ssa.CallCommon{Value: f} 909 | state := HandleCallCommon(ctx, &entryCallCommon, f.Pos()) 910 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses) 911 | require.NoError(t, err) 912 | filteredAccesses := pointerAnalysis.FilterDuplicates(conflictingAccesses) 913 | assert.Len(t, filteredAccesses, 1) 914 | 915 | gas := FindMultipleGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 916 | val, ok := ga.Value.(*ssa.Global) 917 | if !ok { 918 | return false 919 | } 920 | if GetGlobalString(val) != "count" { 921 | return false 922 | } 923 | return true 924 | }, 4) 925 | for _, ga := range gas { 926 | assert.Len(t, ga.State.StackTrace.Iter(), 2) 927 | } 928 | require.Len(t, filteredAccesses, 1) 929 | assert.Subset(t, gas, conflictingAccesses[0]) 930 | } 931 | 932 | func Test_HandleFunction_NestedFunctions(t *testing.T) { 933 | f, _ := LoadMain(t, "./testdata/Functions/General/NestedFunctions/prog1.go") 934 | ctx := domain.NewEmptyContext() 935 | state := HandleFunction(ctx, f) 936 | assert.Len(t, state.Lockset.Locks, 1) 937 | assert.Len(t, state.Lockset.Unlocks, 2) 938 | 939 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 940 | if !IsGARead(ga) { 941 | return false 942 | } 943 | val, ok := ga.Value.(*ssa.Const) 944 | if !ok { 945 | return false 946 | } 947 | if val.Int64() != 5 { 948 | return false 949 | } 950 | return true 951 | }) 952 | assert.Len(t, ga.Lockset.Locks, 2) 953 | assert.Len(t, ga.Lockset.Unlocks, 0) 954 | 955 | ga = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 956 | if !IsGARead(ga) { 957 | return false 958 | } 959 | val, ok := ga.Value.(*ssa.Const) 960 | if !ok { 961 | return false 962 | } 963 | if val.Int64() != 6 { 964 | return false 965 | } 966 | return true 967 | }) 968 | assert.Len(t, ga.Lockset.Locks, 3) 969 | assert.Len(t, ga.Lockset.Unlocks, 0) 970 | } 971 | 972 | func Test_HandleFunction_RecursionWithGoroutine(t *testing.T) { 973 | f, _ := LoadMain(t, "./testdata/Functions/General/RecursionWithGoroutine/prog1.go") 974 | ctx := domain.NewEmptyContext() 975 | state := HandleFunction(ctx, f) 976 | 977 | // Should found 2 occurrences since the algorithm should traverse more then once to find conflicting accesses such 978 | // as recursion with a goroutine 979 | _ = FindMultipleGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 980 | if !IsGARead(ga) { 981 | return false 982 | } 983 | val, ok := ga.Value.(*ssa.Const) 984 | if !ok { 985 | return false 986 | } 987 | return val.Int64() == 0 988 | }, 2) 989 | } 990 | 991 | func Test_HandleFunction_Simple(t *testing.T) { 992 | f, _ := LoadMain(t, "./testdata/Functions/General/Simple/prog1.go") 993 | ctx := domain.NewEmptyContext() 994 | state := HandleFunction(ctx, f) 995 | 996 | gas := FindMultipleGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 997 | if !IsGAWrite(ga) { 998 | return false 999 | } 1000 | _, ok := ga.Value.(*ssa.FieldAddr) 1001 | return ok 1002 | }, 2) 1003 | assert.True(t, gas[0].State.MayConcurrent(gas[1].State)) 1004 | } 1005 | 1006 | func Test_HandleFunction_StructMethod(t *testing.T) { 1007 | f, _ := LoadMain(t, "./testdata/Functions/General/StructMethod/prog1.go") 1008 | ctx := domain.NewEmptyContext() 1009 | state := HandleFunction(ctx, f) 1010 | 1011 | gaA := FindMultipleGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 1012 | if !IsGAWrite(ga) { 1013 | return false 1014 | } 1015 | val, ok := ga.Value.(*ssa.FieldAddr) 1016 | if !ok { 1017 | return false 1018 | } 1019 | _, ok = val.X.(*ssa.Alloc) 1020 | return !ok 1021 | }, 1) 1022 | 1023 | gaB := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 1024 | if !IsGARead(ga) { 1025 | return false 1026 | } 1027 | val, ok := ga.Value.(*ssa.FieldAddr) 1028 | if !ok { 1029 | return false 1030 | } 1031 | _, ok = val.X.(*ssa.Parameter) 1032 | return ok 1033 | }) 1034 | assert.True(t, gaA[0].State.MayConcurrent(gaB.State)) 1035 | } 1036 | 1037 | func Test_HandleFunction_DataRaceInterfaceOverChannel(t *testing.T) { 1038 | f, pkg := LoadMain(t, "./testdata/Functions/PointerAnalysis/DataRaceInterfaceOverChannel/prog1.go") 1039 | ctx := domain.NewEmptyContext() 1040 | state := HandleFunction(ctx, f) 1041 | 1042 | gas := FindMultipleGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool { 1043 | if !IsGAWrite(ga) { 1044 | return false 1045 | } 1046 | _, ok := ga.Value.(*ssa.FieldAddr) 1047 | return ok 1048 | }, 2) 1049 | 1050 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses) 1051 | require.NoError(t, err) 1052 | filteredAccesses := pointerAnalysis.FilterDuplicates(conflictingAccesses) 1053 | require.Len(t, filteredAccesses, 1) 1054 | found := false 1055 | for _, ca := range conflictingAccesses { 1056 | if EqualDifferentOrder(gas, ca) { 1057 | found = true 1058 | } 1059 | } 1060 | assert.True(t, found) 1061 | } 1062 | -------------------------------------------------------------------------------- /ssaUtils/Locks.go: -------------------------------------------------------------------------------- 1 | package ssaUtils 2 | 3 | import ( 4 | "github.com/amit-davidson/Chronos/domain" 5 | "github.com/amit-davidson/Chronos/ssaPureUtils" 6 | "go/token" 7 | "golang.org/x/tools/go/ssa" 8 | ) 9 | 10 | func AddLock(funcState *domain.BlockState, call *ssa.CallCommon, isUnlock bool) { 11 | recv := call.Args[0] 12 | mutexPos := ssaPureUtils.GetMutexPos(recv) 13 | lock := map[token.Pos]*ssa.CallCommon{mutexPos: call} 14 | if isUnlock { 15 | funcState.Lockset.UpdateWithNewLockSet(nil, lock) 16 | } else { 17 | funcState.Lockset.UpdateWithNewLockSet(lock, nil) 18 | } 19 | } -------------------------------------------------------------------------------- /ssaUtils/Packages.go: -------------------------------------------------------------------------------- 1 | package ssaUtils 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/amit-davidson/Chronos/domain" 7 | "go/token" 8 | "go/types" 9 | "golang.org/x/tools/go/loader" 10 | "golang.org/x/tools/go/packages" 11 | "golang.org/x/tools/go/ssa" 12 | "golang.org/x/tools/go/ssa/ssautil" 13 | "sort" 14 | "testing" 15 | ) 16 | 17 | var typesCache = make(map[*types.Interface][]*ssa.Function) 18 | var GlobalProgram *ssa.Program 19 | var GlobalModuleName string 20 | 21 | var ErrNoPackages = errors.New("no packages in the path") 22 | var ErrLoadPackages = errors.New("loading the following file contained errors") 23 | 24 | func Create(t *testing.T, path, fileName string) *ssa.Package { 25 | var conf loader.Config 26 | f, err := conf.ParseFile(fileName, nil) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | conf.CreateFromFiles(path, f) 31 | 32 | lprog, err := conf.Load() 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | 37 | // We needn't call Build. 38 | foo := lprog.Package(path).Pkg 39 | return ssautil.CreateProgram(lprog, ssa.SanityCheckFunctions).Package(foo) 40 | } 41 | 42 | func LoadPackage(path, modulePath string) (*ssa.Program, *ssa.Package, error) { 43 | conf1 := packages.Config{ 44 | Mode: packages.LoadAllSyntax, 45 | Dir: modulePath, 46 | } 47 | loadQuery := fmt.Sprintf("file=%s", path) 48 | pkgs, err := packages.Load(&conf1, loadQuery) 49 | if err != nil { 50 | return nil, nil, err 51 | } 52 | if len(pkgs) == 0 { 53 | return nil, nil, fmt.Errorf("%s: %w", path, ErrNoPackages) 54 | } 55 | 56 | if len(pkgs[0].Errors) > 0 { 57 | return nil, nil, fmt.Errorf("%w %s: %s", ErrLoadPackages, path, pkgs[0].Errors[0].Msg) 58 | } 59 | ssaProg, ssaPkgs := ssautil.AllPackages(pkgs, 0) 60 | ssaProg.Build() 61 | ssaPkg := ssaPkgs[0] 62 | return ssaProg, ssaPkg, nil 63 | } 64 | 65 | func GetStackTrace(prog *ssa.Program, ga *domain.GuardedAccess) string { 66 | stack := "" 67 | for _, pos := range ga.State.StackTrace.Iter() { 68 | calculatedPos := prog.Fset.Position(token.Pos(pos)) 69 | stack += calculatedPos.String() 70 | stack += " ->\n" 71 | } 72 | return stack 73 | } 74 | 75 | func GetMethodImplementations(recv types.Type, method *types.Func) []*ssa.Function { 76 | methodImplementations := make([]*ssa.Function, 0) 77 | recvInterface := recv.(*types.Interface) 78 | 79 | if methodImplementations, ok := typesCache[recvInterface]; ok { 80 | return methodImplementations 81 | } 82 | 83 | implementors := make([]types.Type, 0) 84 | for _, typ := range GlobalProgram.RuntimeTypes() { 85 | if types.Implements(typ, recvInterface) { 86 | implementors = append(implementors, typ) 87 | } 88 | } 89 | for _, implementor := range implementors { 90 | setMethods := GlobalProgram.MethodSets.MethodSet(implementor) 91 | method := setMethods.Lookup(method.Pkg(), method.Name()) 92 | methodImpl := GlobalProgram.MethodValue(method) 93 | if methodImpl.Synthetic == "" { 94 | methodImplementations = append(methodImplementations, methodImpl) 95 | } 96 | } 97 | 98 | // Sort by pos to enter previous implementations first. This make the search deterministic and easier for debugging 99 | sortedImplementations := sortMethodImplementations(methodImplementations) 100 | typesCache[recvInterface] = sortedImplementations 101 | return sortedImplementations 102 | } 103 | 104 | func sortMethodImplementations(methodImplementations []*ssa.Function) []*ssa.Function { 105 | posSlice := make([]int, 0) 106 | sortedImplementations := make([]*ssa.Function, 0) 107 | implMap := make(map[int]*ssa.Function) 108 | for _, methodImplementation := range methodImplementations { 109 | pos := methodImplementation.Pos() 110 | implMap[int(pos)] = methodImplementation 111 | posSlice = append(posSlice, int(pos)) 112 | } 113 | sort.Ints(posSlice) 114 | for _, pos := range posSlice { 115 | sortedImplementations = append(sortedImplementations, implMap[pos]) 116 | } 117 | return sortedImplementations 118 | } 119 | -------------------------------------------------------------------------------- /ssaUtils/PreProcess.go: -------------------------------------------------------------------------------- 1 | package ssaUtils 2 | 3 | import ( 4 | "errors" 5 | "github.com/amit-davidson/Chronos/utils/stacks" 6 | "go/types" 7 | "golang.org/x/tools/go/ssa" 8 | "os" 9 | "path" 10 | "strings" 11 | ) 12 | 13 | type FunctionWithLocksPreprocess struct { 14 | FunctionWithLocks map[*types.Signature]bool 15 | locks map[int]struct{} // Is lock exists, and if it's in a conditional path 16 | visitedFuncs *stacks.FunctionStackWithMap 17 | } 18 | 19 | func InitPreProcess(prog *ssa.Program, defaultModulePath string) error { 20 | GlobalProgram = prog 21 | p := strings.TrimSuffix(defaultModulePath, string(os.PathSeparator)) 22 | splittedPath := strings.Split(p, string(os.PathSeparator)) 23 | if len(splittedPath) < 3 { 24 | return errors.New("path to module can be relative or absolute but must contain the format:{VCS}/{organization}/{package}") 25 | } 26 | l := len(splittedPath) 27 | moduleName := path.Join(splittedPath[l-3], splittedPath[l-2], splittedPath[l-1]) 28 | GlobalModuleName = moduleName 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/Defer/DeferredLockAndUnlockIfBranch/prog1.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "math/rand" 5 | "sync" 6 | ) 7 | 8 | var a int 9 | var cond = false 10 | 11 | func main() { 12 | mutex := sync.Mutex{} 13 | if rand.Int() > 0 { 14 | defer mutex.Lock() 15 | } 16 | defer func() { 17 | a = 5 18 | }() 19 | if rand.Int() > 0 { 20 | defer mutex.Unlock() 21 | } 22 | a = 6 23 | } 24 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/Defer/NestedDeferWithLockAndUnlock/prog1.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "math/rand" 5 | "sync" 6 | ) 7 | 8 | var a int 9 | var cond = false 10 | 11 | func main() { 12 | mutex := sync.Mutex{} 13 | if rand.Int() > 0 { 14 | defer mutex.Lock() 15 | } 16 | defer func() { 17 | a = 6 18 | }() 19 | defer func() { 20 | defer mutex.Lock() 21 | defer mutex.Unlock() 22 | }() 23 | } 24 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/Defer/NestedDeferWithLockAndUnlockAndGoroutine/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "math/rand" 5 | "sync" 6 | ) 7 | 8 | var a int 9 | var cond = false 10 | 11 | func main() { 12 | mutex := sync.Mutex{} 13 | if rand.Int() > 0 { 14 | defer mutex.Lock() 15 | } 16 | defer func() { 17 | mutex.Lock() 18 | a = 6 19 | defer mutex.Unlock() 20 | }() 21 | mutex.Lock() 22 | defer func() { 23 | go func() { 24 | a = 7 25 | defer mutex.Unlock() 26 | }() 27 | }() 28 | } 29 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/ForLoops/ForLoopLockInsideLoop/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | func main() { 6 | m := make(map[string]string) 7 | m["2"] = "b" // Second conflicting access. 8 | for _, _ = range m { 9 | //for i, j := range m { 10 | //fmt.Println(i, j) 11 | //if i > "1" { 12 | // fmt.Println(i, j) 13 | //} 14 | mutex := sync.Mutex{} 15 | mutex.Lock() 16 | m["2"] = "c" // Second conflicting access. 17 | //} 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/ForLoops/ForLoopLockOutsideLoop/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | func main() { 6 | m := make(map[string]string) 7 | m["2"] = "b" // Second conflicting access. 8 | mutex := sync.Mutex{} 9 | mutex.Lock() 10 | for _, _ = range m { 11 | //for i, j := range m { 12 | //fmt.Println(i, j) 13 | //if i > "1" { 14 | // fmt.Println(i, j) 15 | //} 16 | m["2"] = "c" // Second conflicting access. 17 | //} 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/ForLoops/NestedForLoopWithRace/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | m := make(map[string]string) 5 | go func() { 6 | for _, _ = range m { 7 | for _, _ = range m { 8 | m["1"] = "a" 9 | } 10 | } 11 | }() 12 | m["2"] = "b" 13 | } 14 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/ForLoops/WhileLoop/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | import "time" 5 | 6 | type myType struct { 7 | A int 8 | } 9 | 10 | func main() { 11 | x := new(myType) 12 | c := make(chan int, 100) 13 | go func() { 14 | for true { 15 | x = new(myType) // write to x 16 | c <- 0 17 | <-c 18 | } 19 | }() 20 | for i := 0; i < 4; i++ { 21 | go func() { 22 | for true { 23 | _ = *x // if exists a race condition, `*x` will visit a wrong memory address, and will panic 24 | c <- 0 25 | <-c 26 | } 27 | }() 28 | } 29 | time.Sleep(time.Second * 10) 30 | fmt.Println("end") 31 | } 32 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/ForLoops/WhileLoopWithoutHeader/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | import "time" 8 | 9 | type myType struct { 10 | A int 11 | } 12 | 13 | var mutex = sync.Mutex{} 14 | func main() { 15 | x := new(myType) 16 | c := make(chan int, 100) 17 | go func() { 18 | for { 19 | mutex.Lock() 20 | x = new(myType) // write to x 21 | c <- 0 22 | <-c 23 | } 24 | }() 25 | for i := 0; i < 4; i++ { 26 | go func() { 27 | for { 28 | _ = *x // if exists a race condition, `*x` will visit a wrong memory address, and will panic 29 | c <- 0 30 | <-c 31 | } 32 | }() 33 | } 34 | time.Sleep(time.Second * 10) 35 | fmt.Println("end") 36 | } 37 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/General/DataRaceGoto/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | var a int 8 | var mutex1 = sync.Mutex{} 9 | 10 | func main() { 11 | go func() { 12 | a = 5 13 | }() 14 | fn1() 15 | } 16 | 17 | func fn1() { 18 | if a > 4 { 19 | goto End 20 | } 21 | return 22 | End: 23 | a = 6 24 | } 25 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/General/DataRaceMap/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool) 5 | m := make(map[string]string) 6 | go func() { 7 | m["1"] = "a" // First conflicting access. 8 | c <- true 9 | }() 10 | m["2"] = "b" // Second conflicting access. 11 | } 12 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/General/DataRaceNestedSameFunction/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | var count int 4 | 5 | func race() { 6 | count++ 7 | } 8 | 9 | func f() { 10 | race() 11 | } 12 | 13 | func main() { 14 | go f() 15 | go f() 16 | } 17 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/General/DataRaceProperty/prog1.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | ) 8 | 9 | type Watchdog struct{ last int64 } 10 | 11 | func (w *Watchdog) KeepAlive() { 12 | w.last = time.Now().UnixNano() // First conflicting access. 13 | } 14 | 15 | func (w *Watchdog) Start() { 16 | go func() { 17 | time.Sleep(time.Second) 18 | // Second conflicting access. 19 | if w.last < time.Now().Add(-10*time.Second).UnixNano() { 20 | fmt.Println("No keepalives for 10 seconds. Dying.") 21 | os.Exit(1) 22 | } 23 | }() 24 | } 25 | 26 | func main() { 27 | wd := Watchdog{} 28 | go wd.KeepAlive() 29 | go wd.Start() 30 | } 31 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/General/DataRaceRecursion/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | var a int 4 | 5 | func main() { 6 | go func() { 7 | a = 5 8 | }() 9 | fn1(0) 10 | } 11 | 12 | func fn1(counter int) { 13 | if counter >= 10 { 14 | return 15 | } 16 | if counter == 8 { 17 | a = 6 18 | } 19 | counter += 1 20 | fn1(counter) 21 | } 22 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/General/DataRaceShadowedErr/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | // ParallelWrite writes data to file1 and file2, returns the errors. 8 | func ParallelWrite(data []byte) chan error { 9 | res := make(chan error, 2) 10 | f1, err := os.Create("file1") 11 | if err != nil { 12 | res <- err 13 | } else { 14 | go func() { 15 | // This err is shared with the main goroutine, 16 | // so the write races with the write below. 17 | _, err = f1.Write(data) 18 | res <- err 19 | f1.Close() 20 | }() 21 | } 22 | f2, err := os.Create("file2") // The second conflicting write to err. 23 | if err != nil { 24 | res <- err 25 | } else { 26 | go func() { 27 | _, err = f2.Write(data) 28 | res <- err 29 | f2.Close() 30 | }() 31 | } 32 | return res 33 | } 34 | 35 | func main() { 36 | ParallelWrite([]byte("1")) 37 | } 38 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/General/DataRaceWithOnlyAlloc/prog1.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | fmt.Println(getNumber()) 9 | } 10 | 11 | func getNumber() int { 12 | var i int 13 | go func() { 14 | i = 5 15 | }() 16 | 17 | return i 18 | } 19 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/General/DataRaceWithSameFunction/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | var count int 8 | 9 | func race() { 10 | count++ 11 | } 12 | 13 | func main() { 14 | go race() 15 | go race() 16 | time.Sleep(1 * time.Second) 17 | } 18 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/General/ElseIf/prog1.go: -------------------------------------------------------------------------------- 1 | package Lock 2 | 3 | import ( 4 | "math/rand" 5 | ) 6 | 7 | var a int 8 | 9 | func main() { 10 | a := rand.Int() 11 | if a > 0 { 12 | a = 3 13 | } else if a == 0 { 14 | a = 4 15 | } else { 16 | a = 5 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/General/NestedFunctions/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | var a int 8 | var mutex1 = sync.Mutex{} 9 | 10 | func main() { 11 | mutex2 := sync.Mutex{} 12 | mutex1.Lock() 13 | mutex2.Lock() 14 | a = 5 15 | fn2() 16 | } 17 | 18 | func fn2() { 19 | mutex2 := sync.Mutex{} 20 | mutex2.Lock() 21 | a = 6 22 | mutex2.Unlock() 23 | mutex1.Unlock() 24 | } 25 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/General/RecursionWithGoroutine/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | func main() { 6 | g := &sync.WaitGroup{} 7 | runs := 5 8 | recur(runs, g) 9 | } 10 | 11 | func recur(iter int, g *sync.WaitGroup) { 12 | if iter <= 0 { 13 | return 14 | } 15 | go recur(iter-1, g) 16 | } 17 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/General/RecursionWithRecover/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | f() 7 | fmt.Println("Returned normally from f.") 8 | } 9 | 10 | func f() { 11 | defer func() { 12 | if r := recover(); r != nil { 13 | fmt.Println("Recovered in f", r) 14 | } 15 | }() 16 | fmt.Println("Calling g.") 17 | g(0) 18 | fmt.Println("Returned normally from g.") 19 | } 20 | 21 | func g(i int) { 22 | if i > 3 { 23 | fmt.Println("Panicking!") 24 | panic(fmt.Sprintf("%v", i)) 25 | } 26 | defer fmt.Println("Defer in g", i) 27 | fmt.Println("Printing in g", i) 28 | g(i + 1) 29 | } 30 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/General/Simple/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type A struct { 4 | X int 5 | } 6 | 7 | func main() { 8 | a := A{} 9 | go func() { 10 | a.X = 1 11 | }() 12 | a.X = 2 13 | } 14 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/General/StructMethod/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Ben struct { 8 | name string 9 | } 10 | 11 | func (b *Ben) Hello() { 12 | fmt.Printf("my name is:%s", b.name) 13 | } 14 | 15 | func main() { 16 | var ben = &Ben{"Ben"} 17 | 18 | go func() { 19 | ben.name = "Jerry" 20 | }() 21 | ben.Hello() 22 | } 23 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/Interfaces/DataRaceIceCreamMaker/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type IceCreamMaker interface { 4 | Hello() 5 | } 6 | 7 | type Ben struct { 8 | name string 9 | } 10 | 11 | func (b *Ben) Hello() { 12 | b.name = "Ben" 13 | } 14 | 15 | func main() { 16 | var ben = &Ben{} 17 | var maker IceCreamMaker = ben 18 | go maker.Hello() 19 | ben.name = "1" 20 | } 21 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/Interfaces/InterfaceWithLock/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | type IceCreamMaker interface { 6 | // Hello greets a customer 7 | Hello() 8 | } 9 | 10 | type Ben struct { 11 | name string 12 | } 13 | 14 | type Jerry struct { 15 | name string 16 | } 17 | 18 | func (b *Ben) Hello() { 19 | mutex.Lock() 20 | b.name = "Ben" 21 | } 22 | 23 | func (j *Jerry) Hello() { 24 | mutex.Lock() 25 | j.name = "Jerry" 26 | } 27 | 28 | var mutex = sync.Mutex{} 29 | 30 | func main() { 31 | var ben = &Ben{} 32 | var maker IceCreamMaker = ben 33 | maker.Hello() 34 | ben.name = "1" 35 | } 36 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/Interfaces/NestedInterface/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Employee interface { 4 | IceCreamMaker 5 | } 6 | 7 | type IceCreamMaker interface { 8 | Hello() 9 | } 10 | 11 | type Jerry struct { 12 | name string 13 | } 14 | 15 | func (j *Jerry) Hello() { 16 | j.name = "Jerry" 17 | } 18 | 19 | func main() { 20 | var ben = &Jerry{} 21 | var employee Employee = ben 22 | employee.Hello() 23 | } 24 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/LocksAndUnlocks/Lock/prog1.go: -------------------------------------------------------------------------------- 1 | package Lock 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | var a int 8 | 9 | func main() { 10 | mutex := sync.Mutex{} 11 | mutex.Lock() 12 | a = 5 13 | } 14 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/LocksAndUnlocks/LockAndUnlock/prog1.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | var a int 8 | 9 | func main() { 10 | mutex := sync.Mutex{} 11 | mutex.Lock() 12 | mutex.Unlock() 13 | a = 5 14 | } 15 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/LocksAndUnlocks/LockAndUnlockIfBranch/prog1.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "math/rand" 5 | "sync" 6 | ) 7 | 8 | var a int 9 | var cond = false 10 | 11 | func main() { 12 | mutex := sync.Mutex{} 13 | if rand.Int() > 0 { 14 | mutex.Lock() 15 | } 16 | a = 5 17 | if rand.Int() > 0 { 18 | mutex.Unlock() 19 | } 20 | a = 6 21 | } 22 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/LocksAndUnlocks/LockInBothBranches/prog1.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "math/rand" 5 | "sync" 6 | ) 7 | 8 | var a int 9 | var cond = false 10 | 11 | func main() { 12 | mutex := sync.Mutex{} 13 | if rand.Int() > 0 { 14 | mutex.Lock() 15 | } else { 16 | mutex.Lock() 17 | } 18 | a = 5 19 | } 20 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/LocksAndUnlocks/LockInsideGoroutine/prog1.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | var a = make(map[string]interface{}, 0) 8 | 9 | func main() { 10 | mutex := sync.Mutex{} 11 | go func() { 12 | mutex.Lock() 13 | a["1"] = make(map[string]interface{}, 0) 14 | }() 15 | a["2"] = make(map[string]interface{}, 0) 16 | } 17 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/LocksAndUnlocks/MultipleLocksNoRace/prog1.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | func main() { 8 | var mu sync.Mutex 9 | var x int16 = 0 10 | _ = x 11 | ch := make(chan bool, 2) 12 | go func() { 13 | mu.Lock() 14 | defer mu.Unlock() 15 | x = 1 16 | ch <- true 17 | }() 18 | go func() { 19 | mu.Lock() 20 | x = 2 21 | mu.Unlock() 22 | ch <- true 23 | }() 24 | <-ch 25 | <-ch 26 | } 27 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/LocksAndUnlocks/MultipleLocksRace/prog1.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | func main() { 8 | var mu sync.Mutex 9 | var x int16 = 0 10 | _ = x 11 | ch := make(chan bool, 2) 12 | go func() { 13 | x = 1 14 | mu.Lock() 15 | defer mu.Unlock() 16 | ch <- true 17 | }() 18 | go func() { 19 | x = 2 20 | mu.Lock() 21 | mu.Unlock() 22 | ch <- true 23 | }() 24 | <-ch 25 | <-ch 26 | } 27 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/LocksAndUnlocks/NestedConditionWithLockInAllBranches/prog1.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "math/rand" 5 | "sync" 6 | ) 7 | 8 | var a = make(map[string]interface{}, 0) 9 | 10 | func main() { 11 | mutex := sync.Mutex{} 12 | if rand.Int() > 0 { 13 | if rand.Int() > 0 { 14 | mutex.Lock() 15 | } else { 16 | mutex.Lock() 17 | } 18 | } else { 19 | mutex.Lock() 20 | } 21 | a["1"] = make(map[string]interface{}, 0) 22 | } 23 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/LocksAndUnlocks/NestedLockInStruct/prog1.go: -------------------------------------------------------------------------------- 1 | package Lock 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | var a int 8 | 9 | type obj3 struct { 10 | obj2 obj2 11 | } 12 | type obj2 struct { 13 | obj1 obj1 14 | } 15 | 16 | type obj1 struct { 17 | a int 18 | mutex sync.Mutex 19 | } 20 | 21 | func main() { 22 | obj3 := obj3{obj2: obj2{obj1: obj1{1, sync.Mutex{}}}} 23 | obj3.obj2.obj1.mutex.Lock() 24 | obj3.obj2.obj1.mutex.Unlock() 25 | a = 5 26 | } 27 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Functions/PointerAnalysis/DataRaceInterfaceOverChannel/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type IceCreamMaker interface { 4 | // Hello greets a customer 5 | Hello() 6 | } 7 | 8 | type Jerry struct { 9 | name string 10 | } 11 | 12 | func (j *Jerry) Hello() { 13 | j.name = "Jerry" 14 | } 15 | 16 | func sayHello() { 17 | var maker IceCreamMaker 18 | maker = <-channel 19 | maker.Hello() 20 | } 21 | 22 | var channel = make(chan IceCreamMaker) 23 | 24 | func main() { 25 | jerry := &Jerry{} 26 | channel <- jerry 27 | go sayHello() 28 | jerry.name = "1" 29 | } 30 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Preprocess/EvenLockAndUnlockAndLockComesLater/EvenLockAndUnlockAndLockComesLater.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | var a int 6 | 7 | func main() { 8 | var mu sync.Mutex 9 | mu.Unlock() 10 | mu.Lock() 11 | a = 5 12 | _ = a 13 | } 14 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Preprocess/LockAndUnlockInDifferentBranches/LockAndUnlockInDifferentBranches.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "math/rand" 5 | "sync" 6 | ) 7 | 8 | var a int 9 | 10 | func main() { 11 | var mu sync.Mutex 12 | if rand.Int() > 0 { 13 | mu.Unlock() 14 | } else { 15 | mu.Lock() 16 | } 17 | a = 5 18 | _ = a 19 | } 20 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Preprocess/LockInAStruct/LockInAStruct.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | var a int 6 | type B struct { 7 | Lock sync.Mutex 8 | } 9 | func main() { 10 | b := B{Lock:sync.Mutex{}} 11 | b.Lock.Lock() 12 | a = 5 13 | _ = a 14 | } 15 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Preprocess/LockInEmbeddedStruct/LockInEmbeddedStruct.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | var a int 6 | 7 | type B struct { 8 | C 9 | } 10 | 11 | type C struct { 12 | Lock sync.Mutex 13 | } 14 | 15 | func main() { 16 | b := B{C: C{ 17 | sync.Mutex{}, 18 | }} 19 | b.Lock.Lock() 20 | a = 5 21 | _ = a 22 | } 23 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Preprocess/MutexInterface/MutexInterface.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | // A Locker represents an object that can be locked and unlocked. 6 | type Locker interface { 7 | Lock() 8 | Unlock() 9 | } 10 | 11 | type A struct { 12 | Lock Locker 13 | } 14 | 15 | func main() { 16 | b := A{Lock: &sync.Mutex{}} 17 | b.Lock.Lock() 18 | } 19 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Preprocess/NestedFunctionWithLock/NestedFunctionWithLock.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | var a int 6 | 7 | func main() { 8 | f() 9 | } 10 | 11 | func f() { 12 | var mu sync.Mutex 13 | mu.Lock() 14 | a = 5 15 | _ = a 16 | } -------------------------------------------------------------------------------- /ssaUtils/testdata/Preprocess/NestedFunctionWithLockButThenUnlock/NestedFunctionWithLockButThenUnlock.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | var mu sync.Mutex 6 | 7 | func main() { 8 | f() 9 | } 10 | 11 | func f() { 12 | g() 13 | mu.Unlock() 14 | } 15 | 16 | func g() { 17 | mu.Lock() 18 | } 19 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Preprocess/RecursionNestedFunctionWithLock/RecursionNestedFunctionWithLock.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | var mu sync.Mutex 6 | 7 | func main() { 8 | f() 9 | } 10 | 11 | func f() { 12 | mu.Lock() 13 | f() 14 | } -------------------------------------------------------------------------------- /ssaUtils/testdata/Preprocess/UnevenLockAndUnlockAndLockComesLater/UnevenLockAndUnlockAndLockComesLater.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | var a int 6 | 7 | func main() { 8 | var mu sync.Mutex 9 | mu.Lock() 10 | mu.Lock() 11 | mu.Unlock() 12 | a = 5 13 | _ = a 14 | } 15 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Preprocess/functionWithEvenLockAndDeferUnlock/functionWithEvenLockAndDeferUnlock.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | var a int 6 | 7 | func main() { 8 | var mu sync.Mutex 9 | mu.Lock() 10 | defer mu.Unlock() 11 | a = 5 12 | _ = a 13 | } 14 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Preprocess/functionWithEvenLockAndUnlock/functionWithEvenLockAndUnlock.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | var a int 6 | 7 | func main() { 8 | var mu sync.Mutex 9 | mu.Lock() 10 | mu.Unlock() 11 | a = 5 12 | _ = a 13 | } 14 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Preprocess/functionWithLock/functionWithLock.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | var a int 6 | 7 | func main() { 8 | var mu sync.Mutex 9 | mu.Lock() 10 | a = 5 11 | _ = a 12 | } 13 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Preprocess/functionWithUnevenLockAndUnlock/functionWithUnevenLockAndUnlock.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | var a int 6 | 7 | func main() { 8 | var mu sync.Mutex 9 | mu.Lock() 10 | mu.Lock() 11 | mu.Unlock() 12 | a = 5 13 | _ = a 14 | } 15 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Preprocess/functionWithUnlock/functionWithUnlock.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | var a int 6 | 7 | func main() { 8 | var mu sync.Mutex 9 | mu.Unlock() 10 | a = 5 11 | _ = a 12 | } 13 | -------------------------------------------------------------------------------- /ssaUtils/testdata/Preprocess/functionWithoutLocks/functionWithoutLocks.go: -------------------------------------------------------------------------------- 1 | package functionWithoutLocks 2 | 3 | var a int 4 | 5 | func main() { 6 | a = 5 7 | _ = a 8 | } 9 | -------------------------------------------------------------------------------- /ssaUtils/testutils.go: -------------------------------------------------------------------------------- 1 | package ssaUtils 2 | 3 | import ( 4 | "go/constant" 5 | "path/filepath" 6 | "runtime" 7 | "testing" 8 | 9 | "github.com/amit-davidson/Chronos/domain" 10 | "github.com/amit-davidson/Chronos/utils" 11 | "github.com/stretchr/testify/require" 12 | "golang.org/x/tools/go/ssa" 13 | ) 14 | 15 | func FindGA(GuardedAccesses []*domain.GuardedAccess, validationFunc func(value *domain.GuardedAccess) bool) *domain.GuardedAccess { 16 | wasFound := false 17 | for _, ga := range GuardedAccesses { 18 | wasFound = validationFunc(ga) 19 | if wasFound { 20 | return ga 21 | } 22 | } 23 | return nil 24 | } 25 | 26 | func FindMultipleGA(GuardedAccesses []*domain.GuardedAccess, validationFunc func(value *domain.GuardedAccess) bool) []*domain.GuardedAccess { 27 | foundGAs := make([]*domain.GuardedAccess, 0) 28 | for _, ga := range GuardedAccesses { 29 | wasFound := validationFunc(ga) 30 | if wasFound { 31 | foundGAs = append(foundGAs, ga) 32 | } 33 | } 34 | return foundGAs 35 | } 36 | 37 | func GetConstString(v *ssa.Const) string { 38 | return constant.StringVal(v.Value) 39 | } 40 | 41 | func GetGlobalString(v *ssa.Global) string { 42 | return v.Name() 43 | } 44 | 45 | func IsGARead(ga *domain.GuardedAccess) bool { 46 | return ga.OpKind == domain.GuardAccessRead 47 | } 48 | 49 | func IsGAWrite(ga *domain.GuardedAccess) bool { 50 | return ga.OpKind == domain.GuardAccessWrite 51 | } 52 | 53 | func FindGAWithFail(t *testing.T, GuardedAccesses []*domain.GuardedAccess, validationFunc func(value *domain.GuardedAccess) bool) *domain.GuardedAccess { 54 | res := FindGA(GuardedAccesses, validationFunc) 55 | require.NotNil(t, res) 56 | return res 57 | } 58 | 59 | func FindMultipleGAWithFail(t *testing.T, GuardedAccesses []*domain.GuardedAccess, validationFunc func(value *domain.GuardedAccess) bool, expectedAmount int) []*domain.GuardedAccess { 60 | res := FindMultipleGA(GuardedAccesses, validationFunc) 61 | require.Equal(t, expectedAmount, len(res)) 62 | return res 63 | } 64 | 65 | func LoadMain(t *testing.T, filePath string) (*ssa.Function, *ssa.Package) { 66 | domain.GoroutineCounter = utils.NewCounter() 67 | domain.GuardedAccessCounter = utils.NewCounter() 68 | domain.PosIDCounter = utils.NewCounter() 69 | 70 | _, ex, _, ok := runtime.Caller(0) 71 | require.True(t, ok) 72 | modulePath := filepath.Dir(filepath.Dir(ex)) 73 | 74 | ssaProg, ssaPkg, err := LoadPackage(filePath, modulePath) 75 | require.NoError(t, err) 76 | f := ssaPkg.Func("main") 77 | err = InitPreProcess(ssaProg, modulePath) 78 | require.NoError(t, err) 79 | return f, ssaPkg 80 | } 81 | 82 | func EqualDifferentOrder(a, b []*domain.GuardedAccess) bool { 83 | if len(a) != len(b) { 84 | return false 85 | } 86 | diff := make(map[int]int, len(a)) 87 | for _, x := range a { 88 | diff[x.ID]++ 89 | } 90 | for _, y := range b { 91 | if _, ok := diff[y.ID]; !ok { 92 | return false 93 | } 94 | diff[y.ID] -= 1 95 | if diff[y.ID] == 0 { 96 | delete(diff, y.ID) 97 | } 98 | } 99 | return len(diff) == 0 100 | } 101 | -------------------------------------------------------------------------------- /tests/stdlib_runner_test.go: -------------------------------------------------------------------------------- 1 | package e2e_tests 2 | 3 | import ( 4 | "fmt" 5 | "github.com/amit-davidson/Chronos/domain" 6 | "github.com/amit-davidson/Chronos/output" 7 | "github.com/amit-davidson/Chronos/pointerAnalysis" 8 | "github.com/amit-davidson/Chronos/ssaUtils" 9 | "github.com/amit-davidson/Chronos/utils" 10 | "github.com/stretchr/testify/require" 11 | "golang.org/x/tools/go/ssa" 12 | "os" 13 | "path/filepath" 14 | "runtime" 15 | "testing" 16 | ) 17 | 18 | func TestStdlib(t *testing.T) { 19 | var testCases = []struct { 20 | name string 21 | testPath string 22 | }{ 23 | {name: "TestNoRaceStackPushPop", testPath: "testdata/stdlib/TestNoRaceStackPushPop/prog1.go"}, 24 | {name: "RaceNestedArrayCopy", testPath: "testdata/stdlib/RaceNestedArrayCopy/prog1.go"}, 25 | {name: "TestNoRaceAsFunc4", testPath: "testdata/stdlib/TestNoRaceAsFunc4/prog1.go"}, 26 | {name: "TestRaceAsFunc3", testPath: "testdata/stdlib/TestRaceAsFunc3/prog1.go"}, 27 | {name: "TestRaceAsFunc2", testPath: "testdata/stdlib/TestRaceAsFunc2/prog1.go"}, 28 | {name: "TestRaceAsFunc1", testPath: "testdata/stdlib/TestRaceAsFunc1/prog1.go"}, 29 | {name: "TestRaceCaseTypeIssue5890", testPath: "testdata/stdlib/TestRaceCaseTypeIssue5890/prog1.go"}, 30 | {name: "TestRaceCaseIssue6418", testPath: "testdata/stdlib/TestRaceCaseIssue6418/prog1.go"}, 31 | {name: "TestRaceCaseFallthrough", testPath: "testdata/stdlib/TestRaceCaseFallthrough/prog1.go"}, 32 | //{name: "TestNoRaceBlank", testPath: "testdata/stdlibNoSuccess/TestNoRaceBlank/prog1.go"}, // blank space 33 | //{name: "TestRaceMethodThunk4", testPath: "testdata/stdlibNoSuccess/TestRaceMethodThunk4/prog1.go"}, // Might be a bug in pointer analysis 34 | {name: "TestRaceMethodThunk3", testPath: "testdata/stdlib/TestRaceMethodThunk3/prog1.go"}, 35 | {name: "TestRaceMethodThunk2", testPath: "testdata/stdlib/TestRaceMethodThunk2/prog1.go"}, 36 | {name: "TestRaceMethodThunk", testPath: "testdata/stdlibNoSuccess/TestRaceMethodThunk/prog1.go"}, // blank space 37 | {name: "TestNoRaceMethodThunk", testPath: "testdata/stdlib/TestNoRaceMethodThunk/prog1.go"}, 38 | {name: "TestRaceNestedStruct", testPath: "testdata/stdlib/TestRaceNestedStruct/prog1.go"}, 39 | {name: "TestNoRaceEmptyStruct", testPath: "testdata/stdlibNoSuccess/TestNoRaceEmptyStruct/prog1.go"}, 40 | //{name: "TestRaceHeapParam", testPath: "testdata/stdlib/TestRaceHeapParam/prog1.go"}, // No ssa param as value. Might be a bug in ssa. 41 | {name: "TestRaceStructInd", testPath: "testdata/stdlib/TestRaceStructInd/prog1.go"}, 42 | {name: "TestRaceAppendSliceStruct", testPath: "testdata/stdlibNoSuccess/TestRaceAppendSliceStruct/prog1.go"}, 43 | {name: "TestRaceSliceStruct", testPath: "testdata/stdlibNoSuccess/TestRaceSliceStruct/prog1.go"}, // same 44 | {name: "TestRaceSliceString", testPath: "testdata/stdlib/TestRaceSliceString/prog1.go"}, 45 | {name: "TestRaceSliceSlice2", testPath: "testdata/stdlib/TestRaceSliceSlice2/prog1.go"}, 46 | {name: "TestRaceSliceSlice", testPath: "testdata/stdlib/TestRaceSliceSlice/prog1.go"}, 47 | {name: "TestRaceBlockAs", testPath: "testdata/stdlib/TestRaceBlockAs/prog1.go"}, 48 | {name: "TestRaceTypeAssert", testPath: "testdata/stdlib/TestRaceTypeAssert/prog1.go"}, 49 | {name: "TestRaceAddrExpr", testPath: "testdata/stdlib/TestRaceAddrExpr/prog1.go"}, 50 | {name: "TestNoRaceAddrExpr", testPath: "testdata/stdlib/TestNoRaceAddrExpr/prog1.go"}, 51 | {name: "TestRaceDeferArg2", testPath: "testdata/stdlib/TestRaceDeferArg2/prog1.go"}, 52 | {name: "TestRaceDeferArg", testPath: "testdata/stdlib/TestRaceDeferArg/prog1.go"}, 53 | {name: "TestRacePanicArg", testPath: "testdata/stdlib/TestRacePanicArg/prog1.go"}, 54 | {name: "TestNoRaceMethodValue", testPath: "testdata/stdlib/TestNoRaceMethodValue/prog1.go"}, 55 | //{name: "TestRaceMethodValue3", testPath: "testdata/stdlib/TestRaceMethodValue3/prog1.go"}, // Might be a bug in pointer analysis 56 | {name: "TestRaceMethodValue2", testPath: "testdata/stdlib/TestRaceMethodValue2/prog1.go"}, 57 | {name: "TestRaceMethodValue", testPath: "testdata/stdlib/TestRaceMethodValue/prog1.go"}, 58 | {name: "TestRaceMethodCall2", testPath: "testdata/stdlib/TestRaceMethodCall2/prog1.go"}, 59 | {name: "TestRaceMethodCall", testPath: "testdata/stdlib/TestRaceMethodCall/prog1.go"}, 60 | {name: "TestRaceFuncCall", testPath: "testdata/stdlib/TestRaceFuncCall/prog1.go"}, 61 | {name: "TestRaceInterCall2", testPath: "testdata/stdlib/TestRaceInterCall2/prog1.go"}, 62 | {name: "TestRaceInterCall", testPath: "testdata/stdlib/TestRaceInterCall/prog1.go"}, 63 | {name: "TestRaceMapInit2", testPath: "testdata/stdlib/TestRaceMapInit2/prog1.go"}, 64 | {name: "TestRaceMapInit", testPath: "testdata/stdlib/TestRaceMapInit/prog1.go"}, 65 | {name: "TestRaceArrayInit", testPath: "testdata/stdlib/TestRaceArrayInit/prog1.go"}, 66 | {name: "TestRaceStructInit", testPath: "testdata/stdlib/TestRaceStructInit/prog1.go"}, 67 | //{name: "TestNoRaceFuncUnlock", testPath: "testdata/stdlibNoSuccess/TestNoRaceFuncUnlock/prog1.go"}, // No pointer analysis for locks 68 | {name: "TestRaceFuncItself", testPath: "testdata/stdlib/TestRaceFuncItself/prog1.go"}, 69 | //{name: "TestNoRaceShortCalc2", testPath: "testdata/stdlibNoSuccess/TestNoRaceShortCalc2/prog1.go"}, // Cant evaluate first part of condition to see the second will never execute 70 | //{name: "TestNoRaceShortCalc", testPath: "testdata/stdlibNoSuccess/TestNoRaceShortCalc/prog1.go"}, // Cant evaluate first part of condition to see the second will never execute 71 | //{name: "TestNoRaceOr", testPath: "testdata/stdlibNoSuccess/TestNoRaceOr/prog1.go"}, // Cant evaluate first part of condition to see the second will never execute 72 | //{name: "TestRaceOr2", testPath: "testdata/stdlib/TestRaceOr2/prog1.go"}, 73 | {name: "TestRaceOr", testPath: "testdata/stdlib/TestRaceOr/prog1.go"}, 74 | {name: "TestNoRaceAnd", testPath: "testdata/stdlibNoSuccess/TestNoRaceAnd/prog1.go"}, // Cant evaluate first part of condition to see the second will never execute 75 | {name: "TestRaceAnd2", testPath: "testdata/stdlib/TestRaceAnd2/prog1.go"}, 76 | {name: "TestRaceAnd", testPath: "testdata/stdlib/TestRaceAnd/prog1.go"}, 77 | //{name: "TestRaceEmptyInterface2", testPath: "testdata/stdlibNoSuccess/TestRaceEmptyInterface2/prog1.go"}, 78 | {name: "TestRaceEmptyInterface1", testPath: "testdata/stdlib/TestRaceEmptyInterface/prog1.go"}, 79 | {name: "TestRaceRune", testPath: "testdata/stdlib/TestRaceRune/prog1.go"}, 80 | {name: "TestRaceIndirection", testPath: "testdata/stdlibNoSuccess/TestRaceIndirection/prog1.go"}, // sync using channels 81 | {name: "TestRaceFuncArgsRW", testPath: "testdata/stdlib/TestRaceFuncArgsRW/prog1.go"}, 82 | {name: "TestNoRaceFuncArgsRW", testPath: "testdata/stdlib/TestNoRaceFuncArgsRW/prog1.go"}, 83 | {name: "TestRaceAppendCapRW", testPath: "testdata/stdlib/TestRaceAppendCapRW/prog1.go"}, 84 | {name: "TestRaceAppendLenRW", testPath: "testdata/stdlib/TestRaceAppendLenRW/prog1.go"}, 85 | {name: "TestRaceAppendRW", testPath: "testdata/stdlib/TestRaceAppendRW/prog1.go"}, 86 | //{name: "TestRacePanic", testPath: "testdata/stdlibNoSuccess/TestRacePanic/prog1.go"}, // cfg is weird because of the recover 87 | {name: "TestRaceFuncVariableWW", testPath: "testdata/stdlib/TestRaceFuncVariableWW/prog1.go"}, 88 | {name: "TestRaceFuncVariableRW", testPath: "testdata/stdlib/TestRaceFuncVariableRW/prog1.go"}, 89 | {name: "TestRaceUnsafePtrRW", testPath: "testdata/stdlib/TestRaceUnsafePtrRW/prog1.go"}, 90 | {name: "TestRaceComplex128WW", testPath: "testdata/stdlib/TestRaceComplex128WW/prog1.go"}, 91 | {name: "TestRaceFloat64WW", testPath: "testdata/stdlib/TestRaceFloat64WW/prog1.go"}, 92 | {name: "TestRaceStringPtrRW", testPath: "testdata/stdlib/TestRaceStringPtrRW/prog1.go"}, 93 | {name: "TestRaceStringRW", testPath: "testdata/stdlib/TestRaceStringRW/prog1.go"}, 94 | {name: "TestRaceIntptrRW", testPath: "testdata/stdlib/TestRaceIntptrRW/prog1.go"}, 95 | {name: "TestRaceError", testPath: "testdata/stdlib/TestRaceError/prog1.go"}, 96 | {name: "TestRaceIfaceConv", testPath: "testdata/stdlib/TestRaceIfaceConv/prog1.go"}, 97 | {name: "TestRaceEfaceConv", testPath: "testdata/stdlib/TestRaceEfaceConv/prog1.go"}, 98 | {name: "TestRaceIfaceCmpNil", testPath: "testdata/stdlib/TestRaceIfaceCmpNil/prog1.go"}, 99 | {name: "TestRaceIfaceCmp", testPath: "testdata/stdlib/TestRaceIfaceCmp/prog1.go"}, 100 | {name: "TestRaceIfaceWW", testPath: "testdata/stdlib/TestRaceIfaceWW/prog1.go"}, // Before write, a read is performed. So the creation confused with the read later. 101 | {name: "TestRaceEfaceWW", testPath: "testdata/stdlib/TestRaceEfaceWW/prog1.go"}, 102 | {name: "TestRaceStructFieldRW3", testPath: "testdata/stdlib/TestRaceStructFieldRW3/prog1.go"}, 103 | {name: "TestRaceStructFieldRW2", testPath: "testdata/stdlib/TestRaceStructFieldRW2/prog1.go"}, 104 | {name: "TestNoRaceStructFieldRW2", testPath: "testdata/stdlib/TestNoRaceStructFieldRW2/prog1.go"}, 105 | {name: "TestNoRaceStructFieldRW1", testPath: "testdata/stdlib/TestNoRaceStructFieldRW1/prog1.go"}, 106 | {name: "TestRaceStructFieldRW1", testPath: "testdata/stdlib/TestRaceStructFieldRW1/prog1.go"}, 107 | {name: "TestRaceStructRW", testPath: "testdata/stdlibNoSuccess/TestRaceStructRW/prog1.go"}, // The compiler optimizes the ssa in a way that instead of allocating on line 16, the fields are modified. It means that according to ssa, there shouldn't be any data race since the end result is the same. Maybe a bug in ssa? 108 | {name: "TestRaceArrayCopy", testPath: "testdata/stdlib/TestRaceArrayCopy/prog1.go"}, 109 | {name: "TestRaceSprint", testPath: "testdata/stdlib/TestRaceSprint/prog1.go"}, 110 | {name: "TestRaceFuncArgument2", testPath: "testdata/stdlib/TestRaceFuncArgument2/prog1.go"}, 111 | {name: "TestRaceFuncArgument", testPath: "testdata/stdlib/TestRaceFuncArgument/prog1.go"}, 112 | {name: "TestNoRaceEnoughRegisters", testPath: "testdata/stdlib/TestNoRaceEnoughRegisters/prog1.go"}, 113 | {name: "TestRaceRotate", testPath: "testdata/stdlib/TestRaceRotate/prog1.go"}, 114 | {name: "TestRaceModConst", testPath: "testdata/stdlib/TestRaceModConst/prog1.go"}, 115 | {name: "TestRaceMod", testPath: "testdata/stdlib/TestRaceMod/prog1.go"}, 116 | {name: "TestRaceDivConst", testPath: "testdata/stdlib/TestRaceDivConst/prog1.go"}, 117 | {name: "TestRaceDiv", testPath: "testdata/stdlib/TestRaceDiv/prog1.go"}, 118 | {name: "TestRaceComplement", testPath: "testdata/stdlib/TestRaceComplement/prog1.go"}, 119 | {name: "TestNoRacePlus", testPath: "testdata/stdlib/TestNoRacePlus/prog1.go"}, 120 | {name: "TestRacePlus2", testPath: "testdata/stdlib/TestRacePlus2/prog1.go"}, 121 | {name: "TestRacePlus", testPath: "testdata/stdlib/TestRacePlus/prog1.go"}, 122 | {name: "TestRaceCaseTypeBody", testPath: "testdata/stdlib/TestRaceCaseTypeBody/prog1.go"}, 123 | {name: "TestRaceCaseType", testPath: "testdata/stdlib/TestRaceCaseType/prog1.go"}, 124 | //{name: "TestNoRaceCaseFallthrough", testPath: "testdata/stdlibNoSuccess/TestNoRaceCaseFallthrough/prog1.go"}, // No way to determine flow as the detector is flow insensitive 125 | {name: "TestRaceCaseBody", testPath: "testdata/stdlib/TestRaceCaseBody/prog1.go"}, 126 | {name: "TestRaceCaseCondition2", testPath: "testdata/stdlib/TestRaceCaseCondition2/prog1.go"}, 127 | {name: "TestRaceCaseCondition", testPath: "testdata/stdlib/TestRaceCaseCondition/prog1.go"}, 128 | {name: "TestRaceInt32RWClosures", testPath: "testdata/stdlib/TestRaceInt32RWClosures/prog1.go"}, 129 | {name: "TestNoRaceIntRWClosures", testPath: "testdata/stdlib/TestNoRaceIntRWClosures/prog1.go"}, 130 | {name: "TestRaceIntRWClosures", testPath: "testdata/stdlib/TestRaceIntRWClosures/prog1.go"}, 131 | {name: "TestRaceIntRWGlobalFuncs", testPath: "testdata/stdlib/TestRaceIntRWGlobalFuncs/prog1.go"}, 132 | {name: "TestNoRaceComp", testPath: "testdata/stdlib/TestNoRaceComp/prog1.go"}, 133 | {name: "TestRaceComp2", testPath: "testdata/stdlib/TestRaceComp2/prog1.go"}, 134 | {name: "TestRaceSelect1", testPath: "testdata/stdlib/TestRaceSelect1/prog1.go"}, 135 | {name: "TestRaceSelect2", testPath: "testdata/stdlib/TestRaceSelect2/prog1.go"}, 136 | {name: "TestRaceSelect3", testPath: "testdata/stdlib/TestRaceSelect3/prog1.go"}, 137 | {name: "TestRaceSelect4", testPath: "testdata/stdlib/TestRaceSelect4/prog1.go"}, 138 | {name: "TestRaceSelect5", testPath: "testdata/stdlib/TestRaceSelect5/prog1.go"}, 139 | //{name: "TestNoRaceSelect1", testPath: "testdata/stdlibNoSuccess/TestNoRaceSelect1/prog1.go"}, // All of the no race use syncing with channels 140 | //{name: "TestNoRaceSelect2", testPath: "testdata/stdlibNoSuccess/TestNoRaceSelect2/prog1.go"}, 141 | //{name: "TestNoRaceSelect3", testPath: "testdata/stdlibNoSuccess/TestNoRaceSelect3/prog1.go"}, 142 | //{name: "TestNoRaceSelect4", testPath: "testdata/stdlibNoSuccess/TestNoRaceSelect4/prog1.go"}, 143 | //{name: "TestNoRaceSelect5", testPath: "testdata/stdlibNoSuccess/TestNoRaceSelect5/prog1.go"}, 144 | {name: "TestRaceUnaddressableMapLen", testPath: "testdata/stdlib/TestRaceUnaddressableMapLen/prog1.go"}, 145 | {name: "TestNoRaceCase", testPath: "testdata/stdlib/TestNoRaceCase/prog1.go"}, 146 | {name: "TestNoRaceRangeIssue5446", testPath: "testdata/stdlib/TestNoRaceRangeIssue5446/prog1.go"}, 147 | //{name: "TestRaceRange", testPath: "testdata/stdlibNoSuccess/TestRaceRange/prog1.go"}, // doesn't work. There's a race between v on line 11 from iter i+1 and 16/18 form iter i and it's not handled properly. 148 | {name: "TestRaceForInit", testPath: "testdata/stdlib/TestRaceForInit/prog1.go"}, 149 | //{name: "TestNoRaceForInit", testPath: "testdata/stdlibNoSuccess/TestNoRaceForInit/prog1.go"}, // flow analysis required 150 | {name: "TestRaceForTest", testPath: "testdata/stdlib/TestRaceForTest/prog1.go"}, 151 | {name: "TestRaceForIncr", testPath: "testdata/stdlib/TestRaceForIncr/prog1.go"}, 152 | {name: "TestNoRaceForIncr", testPath: "testdata/stdlibNoSuccess/TestNoRaceForIncr/prog1.go"}, // flow analysis required 153 | {name: "TestNoRaceHeapReallocation", testPath: "testdata/stdlib/TestNoRaceHeapReallocation/prog1.go"}, 154 | //{name: "TestRaceIssue5567", testPath: "testdata/stdlib/TestRaceIssue5567/prog1.go"}, // There's write inside f.Read on b which is an external package 155 | //{name: "TestRaceIssue5654", testPath: "testdata/stdlib/TestRaceIssue5654/prog1.go"}, // PackageProblem 156 | {name: "TestNoRaceTinyAlloc", testPath: "testdata/stdlib/TestNoRaceTinyAlloc/prog1.go"}, 157 | } 158 | _, ex, _, ok := runtime.Caller(0) 159 | require.True(t, ok) 160 | modulePath := filepath.Dir(filepath.Dir(ex)) 161 | for _, tc := range testCases { 162 | t.Run(tc.name, func(t *testing.T) { 163 | 164 | ssaProg, ssaPkg, err := ssaUtils.LoadPackage(tc.testPath, modulePath) 165 | require.NoError(t, err) 166 | 167 | domain.GoroutineCounter = utils.NewCounter() 168 | domain.GuardedAccessCounter = utils.NewCounter() 169 | domain.PosIDCounter = utils.NewCounter() 170 | 171 | entryFunc := ssaPkg.Func("main") 172 | err = ssaUtils.InitPreProcess(ssaProg, modulePath) 173 | require.NoError(t, err) 174 | 175 | entryCallCommon := ssa.CallCommon{Value: entryFunc} 176 | functionState := ssaUtils.HandleCallCommon(domain.NewEmptyContext(), &entryCallCommon, entryFunc.Pos()) 177 | conflictingGAs, err := pointerAnalysis.Analysis(ssaPkg, functionState.GuardedAccesses) 178 | if err != nil { 179 | fmt.Printf("Error in analysis:%s\n", err) 180 | os.Exit(1) 181 | } 182 | err = output.GenerateError(conflictingGAs, ssaProg) 183 | if err != nil { 184 | fmt.Printf("Error in generating errors:%s\n", err) 185 | os.Exit(1) 186 | } 187 | }) 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/RaceNestedArrayCopy/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Point struct { 4 | x, y int 5 | } 6 | 7 | func main() { 8 | ch := make(chan bool, 1) 9 | type ( 10 | Point32 [2][2][2][2][2]Point 11 | Point1024 [2][2][2][2][2]Point32 12 | Point32k [2][2][2][2][2]Point1024 13 | Point1M [2][2][2][2][2]Point32k 14 | ) 15 | var a, b Point1M 16 | go func() { 17 | a[0][1][0][1][0][1][0][1][0][1][0][1][0][1][0][1][0][1][0][1].y = 1 18 | ch <- true 19 | }() 20 | a = b 21 | <-ch 22 | } 23 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestNoRaceAddrExpr/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool, 1) 5 | x := 0 6 | go func() { 7 | x = 42 8 | c <- true 9 | }() 10 | _ = &x 11 | <-c 12 | } 13 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestNoRaceAsFunc4/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | func main() { 6 | c := make(chan bool, 1) 7 | var mu sync.Mutex 8 | x := 0 9 | _ = x 10 | go func() { 11 | x = func() int { // Write of x must be under the mutex. 12 | mu.Lock() 13 | return 42 14 | }() 15 | mu.Unlock() 16 | c <- true 17 | }() 18 | mu.Lock() 19 | x = 42 20 | mu.Unlock() 21 | <-c 22 | } 23 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestNoRaceCase/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Point struct { 4 | x, y int 5 | } 6 | 7 | func main() { 8 | var y int 9 | for x := -1; x <= 1; x++ { 10 | switch { 11 | case x < 0: 12 | y = -1 13 | case x == 0: 14 | y = 0 15 | case x > 0: 16 | y = 1 17 | } 18 | } 19 | y++ 20 | } 21 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestNoRaceComp/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type P struct { 4 | x, y int 5 | } 6 | 7 | type S struct { 8 | s1, s2 P 9 | } 10 | 11 | func main() { 12 | c := make(chan bool, 1) 13 | var s S 14 | go func() { 15 | s.s2.x = 1 16 | c <- true 17 | }() 18 | s.s2.y = 2 19 | <-c 20 | } 21 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestNoRaceEnoughRegisters/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | // from erf.go 5 | const ( 6 | sa1 = 1 7 | sa2 = 2 8 | sa3 = 3 9 | sa4 = 4 10 | sa5 = 5 11 | sa6 = 6 12 | sa7 = 7 13 | sa8 = 8 14 | ) 15 | var s, S float64 16 | s = 3.1415 17 | S = 1 + s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(sa5+s*(sa6+s*(sa7+s*sa8))))))) 18 | s = S 19 | } 20 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestNoRaceFuncArgsRW/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Point struct { 4 | x, y int 5 | } 6 | 7 | func main() { 8 | ch := make(chan byte, 1) 9 | var x byte 10 | go func(y byte) { 11 | _ = y 12 | ch <- 0 13 | }(x) 14 | x = 1 15 | <-ch 16 | } 17 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestNoRaceHeapReallocation/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "runtime" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | // It is possible that a future implementation 10 | // of memory allocation will ruin this test. 11 | // Increasing n might help in this case, so 12 | // this test is a bit more generic than most of the 13 | // others. 14 | const n = 2 15 | done := make(chan bool, n) 16 | empty := func(p *int) {} 17 | for i := 0; i < n; i++ { 18 | ms := i 19 | go func() { 20 | <-time.After(time.Duration(ms) * time.Millisecond) 21 | runtime.GC() 22 | var x int 23 | empty(&x) // x goes to the heap 24 | done <- true 25 | }() 26 | } 27 | for i := 0; i < n; i++ { 28 | <-done 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestNoRaceIntRWClosures/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y int 5 | _ = y 6 | ch := make(chan int, 1) 7 | 8 | go func() { 9 | y = x 10 | ch <- 1 11 | }() 12 | <-ch 13 | go func() { 14 | x = 1 15 | ch <- 1 16 | }() 17 | <-ch 18 | } 19 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestNoRaceMethodThunk/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Base int 4 | 5 | func (b *Base) Foo() int { 6 | return 42 7 | } 8 | 9 | func (b Base) Bar() int { 10 | return int(b) 11 | } 12 | 13 | func main() { 14 | type Derived struct { 15 | pad int 16 | *Base 17 | } 18 | var d Derived 19 | done := make(chan bool) 20 | go func() { 21 | _ = d.Foo() 22 | done <- true 23 | }() 24 | d = Derived{} 25 | <-done 26 | } 27 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestNoRaceMethodValue/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Inter interface { 4 | Foo(x int) 5 | } 6 | type InterImpl struct { 7 | x, y int 8 | } 9 | 10 | //go:noinline 11 | func (p InterImpl) Foo(x int) { 12 | } 13 | 14 | type InterImpl2 InterImpl 15 | 16 | func (p *InterImpl2) Foo(x int) { 17 | if p == nil { 18 | InterImpl{}.Foo(x) 19 | } 20 | InterImpl(*p).Foo(x) 21 | } 22 | 23 | func main() { 24 | c := make(chan bool, 1) 25 | i := InterImpl2{} 26 | go func() { 27 | i = InterImpl2{} 28 | c <- true 29 | }() 30 | _ = i.Foo // takes the address of i only. 31 | <-c 32 | } 33 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestNoRacePlus/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y, z int 5 | _ = x 6 | ch := make(chan int, 2) 7 | 8 | go func() { 9 | x = y / (z + 1) 10 | ch <- 1 11 | }() 12 | go func() { 13 | y = z 14 | ch <- 1 15 | }() 16 | <-ch 17 | <-ch 18 | } 19 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestNoRaceRangeIssue5446/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | ch := make(chan int, 3) 5 | a := []int{1, 2, 3} 6 | b := []int{4} 7 | // used to insert a spurious instrumentation of a[i] 8 | // and crash. 9 | i := 1 10 | for i, a[i] = range b { 11 | ch <- i 12 | } 13 | close(ch) 14 | } 15 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestNoRaceSelect4/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | type Task struct { 5 | f func() 6 | done chan bool 7 | } 8 | 9 | queue := make(chan Task) 10 | dummy := make(chan bool) 11 | 12 | go func() { 13 | for true { 14 | select { 15 | case t := <-queue: 16 | t.f() 17 | t.done <- true 18 | } 19 | } 20 | }() 21 | 22 | doit := func(f func()) { 23 | done := make(chan bool, 1) 24 | select { 25 | case queue <- Task{f, done}: 26 | case <-dummy: 27 | } 28 | select { 29 | case <-done: 30 | case <-dummy: 31 | } 32 | } 33 | 34 | var x int 35 | doit(func() { 36 | x = 1 37 | }) 38 | _ = x 39 | } 40 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestNoRaceStackPushPop/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type stack []int 4 | 5 | func (s *stack) push(x int) { 6 | *s = append(*s, x) 7 | } 8 | 9 | func (s *stack) pop() int { 10 | i := len(*s) 11 | n := (*s)[i-1] 12 | *s = (*s)[:i-1] 13 | return n 14 | } 15 | 16 | func main() { 17 | var s stack 18 | go func(s *stack) {}(&s) 19 | s.push(1) 20 | x := s.pop() 21 | _ = x 22 | } 23 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestNoRaceStructFieldRW1/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Point struct { 4 | x, y int 5 | } 6 | 7 | type NamedPoint struct { 8 | name string 9 | p Point 10 | } 11 | 12 | func main() { 13 | // Same struct, different variables, no 14 | // pointers. The layout is known (at compile time?) -> 15 | // no read on p 16 | // writes on x and y 17 | p := Point{0, 0} 18 | ch := make(chan bool, 1) 19 | go func() { 20 | p.x = 1 21 | ch <- true 22 | }() 23 | p.y = 1 24 | <-ch 25 | _ = p 26 | } 27 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestNoRaceStructFieldRW2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Point struct { 4 | x, y int 5 | } 6 | 7 | type NamedPoint struct { 8 | name string 9 | p Point 10 | } 11 | 12 | func main() { 13 | // Same as NoRaceStructFieldRW1 14 | // but p is a pointer, so there is a read on p 15 | p := Point{0, 0} 16 | ch := make(chan bool, 1) 17 | go func() { 18 | p.x = 1 19 | ch <- true 20 | }() 21 | p.y = 1 22 | <-ch 23 | _ = p 24 | } 25 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestNoRaceTinyAlloc/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | const P = 4 5 | const N = 1e6 6 | var tinySink *byte 7 | _ = tinySink 8 | done := make(chan bool) 9 | for p := 0; p < P; p++ { 10 | go func() { 11 | for i := 0; i < N; i++ { 12 | var b byte 13 | if b != 0 { 14 | tinySink = &b // make it heap allocated 15 | } 16 | b = 42 17 | } 18 | done <- true 19 | }() 20 | } 21 | for p := 0; p < P; p++ { 22 | <-done 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceAddrExpr/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type AddrT struct { 4 | _ [256]byte 5 | x int 6 | } 7 | 8 | type AddrT2 struct { 9 | _ [512]byte 10 | p *AddrT 11 | } 12 | 13 | func main() { 14 | c := make(chan bool, 1) 15 | a := AddrT2{p: &AddrT{x: 42}} 16 | go func() { 17 | a.p = &AddrT{x: 43} 18 | c <- true 19 | }() 20 | _ = &a.p.x 21 | <-c 22 | } 23 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceAnd/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool) 5 | x, y := 0, 0 6 | go func() { 7 | x = 1 8 | c <- true 9 | }() 10 | if x == 1 && y == 1 { 11 | } 12 | <-c 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceAnd2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool) 5 | x, y := 0, 0 6 | go func() { 7 | x = 1 8 | c <- true 9 | }() 10 | if y == 0 && x == 1 { 11 | } 12 | <-c 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceAppendCapRW/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Point struct { 4 | x, y int 5 | } 6 | 7 | func main() { 8 | a := make([]int, 0) 9 | ch := make(chan string) 10 | go func() { 11 | a = append(a, 1) 12 | ch <- "" 13 | }() 14 | _ = cap(a) 15 | <-ch 16 | } 17 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceAppendLenRW/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Point struct { 4 | x, y int 5 | } 6 | 7 | func main() { 8 | a := make([]int, 0) 9 | ch := make(chan bool) 10 | go func() { 11 | a = append(a, 1) 12 | ch <- true 13 | }() 14 | _ = len(a) 15 | <-ch 16 | } 17 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceAppendRW/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Point struct { 4 | x, y int 5 | } 6 | 7 | func main() { 8 | a := make([]int, 10) 9 | ch := make(chan bool) 10 | go func() { 11 | _ = append(a, 1) 12 | ch <- true 13 | }() 14 | a[0] = 1 15 | <-ch 16 | } 17 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceArrayCopy/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | ch := make(chan bool, 1) 5 | var a [5]int 6 | go func() { 7 | a[3] = 1 8 | ch <- true 9 | }() 10 | a = [5]int{1, 2, 3, 4, 5} 11 | <-ch 12 | } 13 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceArrayInit/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool, 1) 5 | y := 0 6 | go func() { 7 | y = 42 8 | c <- true 9 | }() 10 | x := []int{0, y, 42} 11 | _ = x 12 | <-c 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceAsFunc1/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var s []byte 5 | c := make(chan bool, 1) 6 | go func() { 7 | var err error 8 | s, err = func() ([]byte, error) { 9 | t := []byte("hello world") 10 | return t, nil 11 | }() 12 | c <- true 13 | _ = err 14 | }() 15 | _ = string(s) 16 | <-c 17 | } 18 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceAsFunc2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool, 1) 5 | x := 0 6 | go func() { 7 | func(x int) { 8 | }(x) 9 | c <- true 10 | }() 11 | x = 42 12 | <-c 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceAsFunc3/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | func main() { 6 | c := make(chan bool, 1) 7 | var mu sync.Mutex 8 | x := 0 9 | go func() { 10 | func(x int) { 11 | mu.Lock() 12 | }(x) // Read of x must be outside of the mutex. 13 | mu.Unlock() 14 | c <- true 15 | }() 16 | mu.Lock() 17 | x = 42 18 | mu.Unlock() 19 | <-c 20 | } 21 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceBlockAs/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool, 1) 5 | var x, y int 6 | go func() { 7 | x = 42 8 | c <- true 9 | }() 10 | x, y = y, x 11 | <-c 12 | } 13 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceCaseBody/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y int 5 | _ = y 6 | ch := make(chan int, 2) 7 | 8 | go func() { 9 | y = x 10 | ch <- 1 11 | }() 12 | go func() { 13 | switch { 14 | default: 15 | x = 1 16 | case x == 100: 17 | x = -x 18 | } 19 | ch <- 1 20 | }() 21 | <-ch 22 | <-ch 23 | } 24 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceCaseCondition/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x int = 0 5 | ch := make(chan int, 2) 6 | 7 | go func() { 8 | x = 2 9 | ch <- 1 10 | }() 11 | go func() { 12 | switch x < 2 { 13 | case true: 14 | x = 1 15 | //case false: 16 | // x = 5 17 | } 18 | ch <- 1 19 | }() 20 | <-ch 21 | <-ch 22 | } 23 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceCaseCondition2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | // switch body is rearranged by the compiler so the tests 5 | // passes even if we don't instrument '<' 6 | var x int = 0 7 | ch := make(chan int, 2) 8 | 9 | go func() { 10 | x = 2 11 | ch <- 1 12 | }() 13 | go func() { 14 | switch x < 2 { 15 | case true: 16 | x = 1 17 | case false: 18 | x = 5 19 | } 20 | ch <- 1 21 | }() 22 | <-ch 23 | <-ch 24 | } 25 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceCaseFallthrough/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y, z int 5 | _ = y 6 | ch := make(chan int, 2) 7 | z = 1 8 | 9 | go func() { 10 | y = x 11 | ch <- 1 12 | }() 13 | go func() { 14 | switch { 15 | case z == 1: 16 | fallthrough 17 | case z == 2: 18 | x = 2 19 | } 20 | ch <- 1 21 | }() 22 | 23 | <-ch 24 | <-ch 25 | } 26 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceCaseIssue6418/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | m := map[string]map[string]string{ 5 | "a": { 6 | "b": "c", 7 | }, 8 | } 9 | ch := make(chan int) 10 | go func() { 11 | m["a"]["x"] = "y" 12 | ch <- 1 13 | }() 14 | switch m["a"]["b"] { 15 | } 16 | <-ch 17 | } 18 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceCaseType/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y int 5 | var i interface{} = x 6 | c := make(chan int, 1) 7 | go func() { 8 | switch i.(type) { 9 | case nil: 10 | case int: 11 | } 12 | c <- 1 13 | }() 14 | i = y 15 | <-c 16 | } 17 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceCaseTypeBody/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y int 5 | var i interface{} = &x 6 | c := make(chan int, 1) 7 | go func() { 8 | switch i := i.(type) { 9 | case nil: 10 | case *int: 11 | *i = y 12 | } 13 | c <- 1 14 | }() 15 | x = y 16 | <-c 17 | } 18 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceCaseTypeIssue5890/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y int 5 | m := make(map[int]map[int]interface{}) 6 | m[0] = make(map[int]interface{}) 7 | c := make(chan int, 1) 8 | go func() { 9 | switch i := m[0][1].(type) { 10 | case nil: 11 | case *int: 12 | *i = x 13 | } 14 | c <- 1 15 | }() 16 | m[0][1] = y 17 | <-c 18 | } 19 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceComp2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type P struct { 4 | x, y int 5 | } 6 | 7 | type S struct { 8 | s1, s2 P 9 | } 10 | 11 | func main() { 12 | c := make(chan bool, 1) 13 | var s S 14 | go func() { 15 | s.s1.x = 1 16 | c <- true 17 | }() 18 | s = S{} 19 | <-c 20 | } 21 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceComplement/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y, z int 5 | _ = x 6 | ch := make(chan int, 2) 7 | 8 | go func() { 9 | x = ^y 10 | ch <- 1 11 | }() 12 | go func() { 13 | y = ^z 14 | ch <- 1 15 | }() 16 | <-ch 17 | <-ch 18 | } 19 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceComplex128WW/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y complex128 5 | ch := make(chan bool, 1) 6 | go func() { 7 | x = 2 + 2i 8 | ch <- true 9 | }() 10 | x = 4 + 4i 11 | <-ch 12 | 13 | y = x 14 | x = y 15 | } 16 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceDeferArg/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type DeferT int 4 | 5 | func (d DeferT) Foo() { 6 | } 7 | 8 | func main() { 9 | c := make(chan bool, 1) 10 | x := 0 11 | go func() { 12 | x = 42 13 | c <- true 14 | }() 15 | func() { 16 | defer func(x int) { 17 | }(x) 18 | }() 19 | <-c 20 | } 21 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceDeferArg2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type DeferT int 4 | 5 | func (d DeferT) Foo() { 6 | } 7 | 8 | func main() { 9 | c := make(chan bool, 1) 10 | var x DeferT 11 | go func() { 12 | var y DeferT 13 | x = y 14 | c <- true 15 | }() 16 | func() { 17 | defer x.Foo() 18 | }() 19 | <-c 20 | } 21 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceDiv/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y, z int 5 | _ = x 6 | ch := make(chan int, 2) 7 | 8 | go func() { 9 | x = y / (z + 1) 10 | ch <- 1 11 | }() 12 | go func() { 13 | y = z 14 | ch <- 1 15 | }() 16 | <-ch 17 | <-ch 18 | } 19 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceDivConst/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y, z uint32 5 | _ = x 6 | ch := make(chan int, 2) 7 | 8 | go func() { 9 | x = y / 3 // involves only a HMUL node 10 | ch <- 1 11 | }() 12 | go func() { 13 | y = z 14 | ch <- 1 15 | }() 16 | <-ch 17 | <-ch 18 | } 19 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceEfaceConv/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool) 5 | v := 0 6 | go func() { 7 | go func(x interface{}) { 8 | }(v) 9 | c <- true 10 | }() 11 | v = 42 12 | <-c 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceEfaceWW/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type DummyWriter struct { 4 | state int 5 | } 6 | type Writer interface { 7 | Write(p []byte) (n int) 8 | } 9 | 10 | func (d DummyWriter) Write(p []byte) (n int) { 11 | return 0 12 | } 13 | 14 | func main() { 15 | var a, b interface{} 16 | ch := make(chan bool, 1) 17 | go func() { 18 | a = 1 19 | ch <- true 20 | }() 21 | a = 2 22 | <-ch 23 | _, _ = a, b 24 | } 25 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceEmptyInterface/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool) 5 | var x interface{} 6 | go func() { 7 | x = nil 8 | c <- true 9 | }() 10 | _ = x 11 | <-c 12 | } 13 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceEmptyInterface2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | type Point struct { 6 | x, y int 7 | } 8 | 9 | var mutex sync.Mutex 10 | 11 | func main() { 12 | c := make(chan bool) 13 | var x interface{} 14 | go func() { 15 | x = &Point{} 16 | c <- true 17 | }() 18 | mutex.Lock() 19 | _ = x 20 | <-c 21 | } 22 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceError/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | ch := make(chan bool, 1) 5 | var err error 6 | go func() { 7 | err = nil 8 | ch <- true 9 | }() 10 | _ = err 11 | <-ch 12 | } 13 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceFloat64WW/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y float64 5 | ch := make(chan bool, 1) 6 | go func() { 7 | x = 1.0 8 | ch <- true 9 | }() 10 | x = 2.0 11 | <-ch 12 | 13 | y = x 14 | x = y 15 | } 16 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceForIncr/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | done := make(chan bool) 5 | c := make(chan bool) 6 | x := 0 7 | go func() { 8 | for { 9 | _, ok := <-c 10 | if !ok { 11 | done <- true 12 | return 13 | } 14 | x++ 15 | } 16 | }() 17 | for i := 0; i < 10; x++ { 18 | i++ 19 | c <- true 20 | } 21 | close(c) 22 | <-done 23 | } 24 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceForInit/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan int) 5 | x := 0 6 | go func() { 7 | c <- x 8 | }() 9 | for x = 42; false; { 10 | } 11 | <-c 12 | } 13 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceForTest/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | done := make(chan bool) 5 | c := make(chan bool) 6 | stop := false 7 | go func() { 8 | for { 9 | _, ok := <-c 10 | if !ok { 11 | done <- true 12 | return 13 | } 14 | stop = true 15 | } 16 | }() 17 | for !stop { 18 | c <- true 19 | } 20 | close(c) 21 | <-done 22 | } 23 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceFuncArgsRW/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Point struct { 4 | x, y int 5 | } 6 | 7 | func main() { 8 | ch := make(chan byte, 1) 9 | var x byte 10 | go func(y *byte) { 11 | _ = *y 12 | ch <- 0 13 | }(&x) 14 | x = 1 15 | <-ch 16 | } 17 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceFuncArgument/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // emptyFunc should not be inlined. 6 | func emptyFunc(x int) { 7 | if false { 8 | fmt.Println(x) 9 | } 10 | } 11 | 12 | func main() { 13 | var x int 14 | ch := make(chan bool, 1) 15 | go func() { 16 | emptyFunc(x) 17 | ch <- true 18 | }() 19 | x = 1 20 | <-ch 21 | } 22 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceFuncArgument2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x int 5 | ch := make(chan bool, 2) 6 | go func() { 7 | x = 42 8 | ch <- true 9 | }() 10 | go func(y int) { 11 | ch <- true 12 | }(x) 13 | <-ch 14 | <-ch 15 | } 16 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceFuncCall/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Inter interface { 4 | Foo(x int) 5 | } 6 | type InterImpl struct { 7 | x, y int 8 | } 9 | 10 | //go:noinline 11 | func (p InterImpl) Foo(x int) { 12 | } 13 | 14 | type InterImpl2 InterImpl 15 | 16 | func (p *InterImpl2) Foo(x int) { 17 | if p == nil { 18 | InterImpl{}.Foo(x) 19 | } 20 | InterImpl(*p).Foo(x) 21 | } 22 | 23 | func main() { 24 | c := make(chan bool, 1) 25 | f := func(x, y int) {} 26 | x, y := 0, 0 27 | go func() { 28 | y = 42 29 | c <- true 30 | }() 31 | f(x, y) 32 | <-c 33 | } 34 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceFuncItself/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool) 5 | f := func() {} 6 | go func() { 7 | f() 8 | c <- true 9 | }() 10 | f = func() {} 11 | <-c 12 | } 13 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceFuncVariableRW/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var f func(x int) int 5 | f = func(x int) int { 6 | return x * x 7 | } 8 | ch := make(chan bool, 1) 9 | go func() { 10 | f = func(x int) int { 11 | return x 12 | } 13 | ch <- true 14 | }() 15 | y := f(1) 16 | <-ch 17 | x := y 18 | y = x 19 | <-ch 20 | } 21 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceFuncVariableWW/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var f func(x int) int 5 | _ = f 6 | ch := make(chan bool, 1) 7 | go func() { 8 | f = func(x int) int { 9 | return x 10 | } 11 | ch <- true 12 | }() 13 | f = func(x int) int { 14 | return x * x 15 | } 16 | <-ch 17 | } 18 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceIfaceCmp/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type DummyWriter struct { 4 | state int 5 | } 6 | type Writer interface { 7 | Write(p []byte) (n int) 8 | } 9 | 10 | func (d DummyWriter) Write(p []byte) (n int) { 11 | return 0 12 | } 13 | 14 | func main() { 15 | var a, b Writer 16 | a = DummyWriter{1} 17 | ch := make(chan bool, 1) 18 | go func() { 19 | a = DummyWriter{1} 20 | ch <- true 21 | }() 22 | _ = a == b 23 | <-ch 24 | } 25 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceIfaceCmpNil/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type DummyWriter struct { 4 | state int 5 | } 6 | type Writer interface { 7 | Write(p []byte) (n int) 8 | } 9 | 10 | func (d DummyWriter) Write(p []byte) (n int) { 11 | return 0 12 | } 13 | 14 | func main() { 15 | var a Writer 16 | a = DummyWriter{1} 17 | ch := make(chan bool, 1) 18 | go func() { 19 | a = DummyWriter{1} 20 | ch <- true 21 | }() 22 | _ = a == nil 23 | <-ch 24 | } 25 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceIfaceConv/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type OsFile struct{} 4 | 5 | func (*OsFile) Read() { 6 | } 7 | 8 | type IoReader interface { 9 | Read() 10 | } 11 | 12 | func main() { 13 | c := make(chan bool) 14 | f := &OsFile{} 15 | go func() { 16 | go func(x IoReader) { 17 | }(f) 18 | c <- true 19 | }() 20 | f = &OsFile{} 21 | <-c 22 | } 23 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceIfaceWW/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type DummyWriter struct { 4 | state int 5 | } 6 | type Writer interface { 7 | Write(p []byte) (n int) 8 | } 9 | 10 | func (d DummyWriter) Write(p []byte) (n int) { 11 | return 0 12 | } 13 | 14 | func main() { 15 | var a, b Writer 16 | ch := make(chan bool, 1) 17 | go func() { 18 | a = DummyWriter{1} 19 | ch <- true 20 | }() 21 | a = DummyWriter{2} 22 | <-ch 23 | b = a 24 | a = b 25 | } 26 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceInt32RWClosures/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y int32 5 | _ = y 6 | ch := make(chan bool, 2) 7 | 8 | go func() { 9 | y = x 10 | ch <- true 11 | }() 12 | go func() { 13 | x = 1 14 | ch <- true 15 | }() 16 | <-ch 17 | <-ch 18 | } 19 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceIntRWClosures/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y int 5 | _ = y 6 | ch := make(chan int, 2) 7 | 8 | go func() { 9 | y = x 10 | ch <- 1 11 | }() 12 | go func() { 13 | x = 1 14 | ch <- 1 15 | }() 16 | <-ch 17 | <-ch 18 | } 19 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceIntRWGlobalFuncs/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | var GlobalX, GlobalY int = 0, 0 4 | var GlobalCh chan int = make(chan int, 2) 5 | 6 | func GlobalFunc1() { 7 | GlobalY = GlobalX 8 | GlobalCh <- 1 9 | } 10 | 11 | func GlobalFunc2() { 12 | GlobalX = 1 13 | GlobalCh <- 1 14 | } 15 | 16 | func main() { 17 | go GlobalFunc1() 18 | go GlobalFunc2() 19 | <-GlobalCh 20 | <-GlobalCh 21 | } 22 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceInterCall/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Inter interface { 4 | Foo(x int) 5 | } 6 | type InterImpl struct { 7 | x, y int 8 | } 9 | 10 | //go:noinline 11 | func (p InterImpl) Foo(x int) { 12 | } 13 | 14 | type InterImpl2 InterImpl 15 | 16 | func (p *InterImpl2) Foo(x int) { 17 | if p == nil { 18 | InterImpl{}.Foo(x) 19 | } 20 | InterImpl(*p).Foo(x) 21 | } 22 | 23 | func main() { 24 | c := make(chan bool, 1) 25 | p := InterImpl{} 26 | var x Inter = p 27 | go func() { 28 | p2 := InterImpl{} 29 | x = p2 30 | c <- true 31 | }() 32 | x.Foo(0) 33 | <-c 34 | } 35 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceInterCall2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Inter interface { 4 | Foo(x int) 5 | } 6 | type InterImpl struct { 7 | x, y int 8 | } 9 | 10 | //go:noinline 11 | func (p InterImpl) Foo(x int) { 12 | } 13 | 14 | type InterImpl2 InterImpl 15 | 16 | func (p *InterImpl2) Foo(x int) { 17 | if p == nil { 18 | InterImpl{}.Foo(x) 19 | } 20 | InterImpl(*p).Foo(x) 21 | } 22 | 23 | func main() { 24 | c := make(chan bool, 1) 25 | p := InterImpl{} 26 | var x Inter = p 27 | z := 0 28 | go func() { 29 | z = 42 30 | c <- true 31 | }() 32 | x.Foo(z) 33 | <-c 34 | } 35 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceIntptrRW/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y int 5 | var p *int = &x 6 | ch := make(chan bool, 1) 7 | go func() { 8 | *p = 5 9 | ch <- true 10 | }() 11 | y = *p 12 | x = y 13 | <-ch 14 | } 15 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceIssue5564/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | func main() { 8 | text := `Friends, Romans, countrymen, lend me your ears; 9 | I come to bury Caesar, not to praise him. 10 | The evil that men do lives after them; 11 | The good is oft interred with their bones; 12 | So let it be with Caesar. The noble Brutus 13 | Hath told you Caesar was ambitious: 14 | If it were so, it was a grievous fault, 15 | And grievously hath Caesar answer'd it. 16 | Here, under leave of Brutus and the rest - 17 | For Brutus is an honourable man; 18 | So are they all, all honourable men - 19 | Come I to speak in Caesar's funeral. 20 | He was my friend, faithful and just to me: 21 | But Brutus says he was ambitious; 22 | And Brutus is an honourable man.` 23 | 24 | data := bytes.NewBufferString(text) 25 | in := make(chan []byte) 26 | 27 | go func() { 28 | buf := make([]byte, 16) 29 | var n int 30 | var err error 31 | for ; err == nil; n, err = data.Read(buf) { 32 | in <- buf[:n] 33 | } 34 | close(in) 35 | }() 36 | res := "" 37 | for s := range in { 38 | res += string(s) 39 | } 40 | _ = res 41 | } 42 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceIssue5567/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/sha1" 5 | "io" 6 | "os" 7 | "runtime" 8 | ) 9 | 10 | func main() { 11 | defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) 12 | in := make(chan []byte) 13 | res := make(chan error) 14 | go func() { 15 | var err error 16 | defer func() { 17 | close(in) 18 | res <- err 19 | }() 20 | path := "mop_test.go" 21 | f, err := os.Open(path) 22 | if err != nil { 23 | return 24 | } 25 | defer f.Close() 26 | var n, total int 27 | b := make([]byte, 17) // the race is on b buffer 28 | for err == nil { 29 | n, err = f.Read(b) 30 | total += n 31 | if n > 0 { 32 | in <- b[:n] 33 | } 34 | } 35 | if err == io.EOF { 36 | err = nil 37 | } 38 | }() 39 | h := sha1.New() 40 | for b := range in { 41 | h.Write(b) 42 | } 43 | _ = h.Sum(nil) 44 | err := <-res 45 | if err != nil { 46 | os.Exit(1) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceMapInit/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool, 1) 5 | y := 0 6 | go func() { 7 | y = 42 8 | c <- true 9 | }() 10 | x := map[int]int{0: 42, y: 42} 11 | _ = x 12 | <-c 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceMapInit2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool, 1) 5 | y := 0 6 | go func() { 7 | y = 42 8 | c <- true 9 | }() 10 | x := map[int]int{0: 42, 42: y} 11 | _ = x 12 | <-c 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceMethodCall/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Inter interface { 4 | Foo(x int) 5 | } 6 | type InterImpl struct { 7 | x, y int 8 | } 9 | 10 | //go:noinline 11 | func (p InterImpl) Foo(x int) { 12 | } 13 | 14 | type InterImpl2 InterImpl 15 | 16 | func (p *InterImpl2) Foo(x int) { 17 | if p == nil { 18 | InterImpl{}.Foo(x) 19 | } 20 | InterImpl(*p).Foo(x) 21 | } 22 | 23 | func main() { 24 | c := make(chan bool, 1) 25 | i := InterImpl{} 26 | x := 0 27 | go func() { 28 | x = 42 29 | c <- true 30 | }() 31 | i.Foo(x) 32 | <-c 33 | } 34 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceMethodCall2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Inter interface { 4 | Foo(x int) 5 | } 6 | type InterImpl struct { 7 | x, y int 8 | } 9 | 10 | //go:noinline 11 | func (p InterImpl) Foo(x int) { 12 | } 13 | 14 | type InterImpl2 InterImpl 15 | 16 | func (p *InterImpl2) Foo(x int) { 17 | if p == nil { 18 | InterImpl{}.Foo(x) 19 | } 20 | InterImpl(*p).Foo(x) 21 | } 22 | 23 | func main() { 24 | c := make(chan bool, 1) 25 | i := &InterImpl{} 26 | go func() { 27 | i = &InterImpl{} 28 | c <- true 29 | }() 30 | i.Foo(0) 31 | <-c 32 | } 33 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceMethodThunk2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Base int 4 | 5 | func (b *Base) Foo() int { 6 | return 42 7 | } 8 | 9 | func (b Base) Bar() int { 10 | return int(b) 11 | } 12 | 13 | func main() { 14 | type Derived struct { 15 | pad int 16 | Base 17 | } 18 | var d Derived 19 | done := make(chan bool) 20 | go func() { 21 | _ = d.Bar() 22 | done <- true 23 | }() 24 | d = Derived{} 25 | <-done 26 | } 27 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceMethodThunk3/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Base int 4 | 5 | func (b *Base) Foo() int { 6 | return 42 7 | } 8 | 9 | func (b Base) Bar() int { 10 | return int(b) 11 | } 12 | 13 | func main() { 14 | type Derived struct { 15 | pad int 16 | *Base 17 | } 18 | var d Derived 19 | d.Base = new(Base) 20 | done := make(chan bool) 21 | go func() { 22 | _ = d.Bar() 23 | done <- true 24 | }() 25 | d.Base = new(Base) 26 | <-done 27 | } 28 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceMethodValue/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Inter interface { 4 | Foo(x int) 5 | } 6 | type InterImpl struct { 7 | x, y int 8 | } 9 | 10 | //go:noinline 11 | func (p InterImpl) Foo(x int) { 12 | } 13 | 14 | type InterImpl2 InterImpl 15 | 16 | func (p *InterImpl2) Foo(x int) { 17 | if p == nil { 18 | InterImpl{}.Foo(x) 19 | } 20 | InterImpl(*p).Foo(x) 21 | } 22 | 23 | func main() { 24 | c := make(chan bool, 1) 25 | i := InterImpl{} 26 | go func() { 27 | i = InterImpl{} 28 | c <- true 29 | }() 30 | _ = i.Foo 31 | <-c 32 | } 33 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceMethodValue2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Inter interface { 4 | Foo(x int) 5 | } 6 | type InterImpl struct { 7 | x, y int 8 | } 9 | 10 | //go:noinline 11 | func (p InterImpl) Foo(x int) { 12 | } 13 | 14 | type InterImpl2 InterImpl 15 | 16 | func (p *InterImpl2) Foo(x int) { 17 | if p == nil { 18 | InterImpl{}.Foo(x) 19 | } 20 | InterImpl(*p).Foo(x) 21 | } 22 | 23 | func main() { 24 | c := make(chan bool, 1) 25 | var i Inter = InterImpl{} 26 | go func() { 27 | i = InterImpl{} 28 | c <- true 29 | }() 30 | _ = i.Foo 31 | <-c 32 | } 33 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceMethodValue3/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Inter interface { 4 | Foo(x int) 5 | } 6 | type InterImpl struct { 7 | x, y int 8 | } 9 | 10 | //go:noinline 11 | func (p InterImpl) Foo(x int) { 12 | } 13 | 14 | type InterImpl2 InterImpl 15 | 16 | func (p *InterImpl2) Foo(x int) { 17 | if p == nil { 18 | InterImpl{}.Foo(x) 19 | } 20 | InterImpl(*p).Foo(x) 21 | } 22 | 23 | func main() { 24 | c := make(chan bool, 1) 25 | i := &InterImpl{} 26 | go func() { 27 | *i = InterImpl{} 28 | c <- true 29 | }() 30 | _ = i.Foo // dereferences i. 31 | <-c 32 | } 33 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceMod/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y, z int 5 | _ = x 6 | ch := make(chan int, 2) 7 | 8 | go func() { 9 | x = y % (z + 1) 10 | ch <- 1 11 | }() 12 | go func() { 13 | y = z 14 | ch <- 1 15 | }() 16 | <-ch 17 | <-ch 18 | } 19 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceModConst/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y, z int 5 | _ = x 6 | ch := make(chan int, 2) 7 | 8 | go func() { 9 | x = y % 3 10 | ch <- 1 11 | }() 12 | go func() { 13 | y = z 14 | ch <- 1 15 | }() 16 | <-ch 17 | <-ch 18 | } 19 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceNestedStruct/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | type X struct { 5 | x, y int 6 | } 7 | type Y struct { 8 | x X 9 | } 10 | c := make(chan Y) 11 | var t Y 12 | go func() { 13 | c <- t 14 | }() 15 | //time.Sleep(2*time.Second) 16 | t.x.y = 42 17 | val := <-c 18 | _ = val 19 | } 20 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceOr/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool) 5 | x, y := 0, 0 6 | go func() { 7 | x = 1 8 | c <- true 9 | }() 10 | if x == 1 || y == 1 { 11 | } 12 | <-c 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceOr2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool) 5 | x, y := 0, 0 6 | go func() { 7 | x = 1 8 | c <- true 9 | }() 10 | if x == 1 || y == 1 { 11 | } 12 | <-c 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRacePanicArg/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "errors" 4 | 5 | func main() { 6 | c := make(chan bool, 1) 7 | err := errors.New("err") 8 | go func() { 9 | err = errors.New("err2") 10 | c <- true 11 | }() 12 | defer func() { 13 | recover() 14 | <-c 15 | }() 16 | panic(err) 17 | } 18 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRacePlus/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y, z int 5 | _ = y 6 | ch := make(chan int, 2) 7 | 8 | go func() { 9 | y = x + z 10 | ch <- 1 11 | }() 12 | go func() { 13 | y = x + z + z 14 | ch <- 1 15 | }() 16 | <-ch 17 | <-ch 18 | } 19 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRacePlus2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y, z int 5 | _ = y 6 | ch := make(chan int, 2) 7 | 8 | go func() { 9 | x = 1 10 | ch <- 1 11 | }() 12 | go func() { 13 | y = +x + z 14 | ch <- 1 15 | }() 16 | <-ch 17 | <-ch 18 | } 19 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceRotate/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y, z uint32 5 | _ = x 6 | ch := make(chan int, 2) 7 | 8 | go func() { 9 | x = y<<12 | y>>20 10 | ch <- 1 11 | }() 12 | go func() { 13 | y = z 14 | ch <- 1 15 | }() 16 | <-ch 17 | <-ch 18 | } 19 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceRune/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool) 5 | var x rune 6 | go func() { 7 | x = 1 8 | c <- true 9 | }() 10 | _ = x 11 | <-c 12 | } 13 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceSelect1/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x int 5 | _ = x 6 | compl := make(chan bool, 2) 7 | c := make(chan bool) 8 | c1 := make(chan bool) 9 | 10 | go func() { 11 | <-c 12 | <-c 13 | }() 14 | f := func() { 15 | select { 16 | case c <- true: 17 | case c1 <- true: 18 | } 19 | x = 1 20 | compl <- true 21 | } 22 | go f() 23 | go f() 24 | <-compl 25 | <-compl 26 | } 27 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceSelect2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x int 5 | _ = x 6 | compl := make(chan bool) 7 | c := make(chan bool) 8 | c1 := make(chan bool) 9 | go func() { 10 | x = 1 11 | select { 12 | case <-c: 13 | case <-c1: 14 | } 15 | compl <- true 16 | }() 17 | close(c) 18 | x = 2 19 | <-compl 20 | } 21 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceSelect3/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x int 5 | _ = x 6 | compl := make(chan bool) 7 | c := make(chan bool) 8 | c1 := make(chan bool) 9 | go func() { 10 | x = 1 11 | select { 12 | case c <- true: 13 | case c1 <- true: 14 | } 15 | compl <- true 16 | }() 17 | x = 2 18 | select { 19 | case <-c: 20 | } 21 | <-compl 22 | } 23 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceSelect4/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | done := make(chan bool, 1) 5 | var x int 6 | go func() { 7 | select { 8 | default: 9 | x = 2 10 | } 11 | done <- true 12 | }() 13 | _ = x 14 | <-done 15 | } 16 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceSelect5/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | done := make(chan bool, 1) 5 | c1 := make(chan bool, 1) 6 | c2 := make(chan bool) 7 | var x, y int 8 | go func() { 9 | select { 10 | case c1 <- true: 11 | x = 1 12 | case c2 <- true: 13 | y = 1 14 | } 15 | done <- true 16 | }() 17 | _ = x 18 | _ = y 19 | <-done 20 | } 21 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceSliceSlice/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool, 1) 5 | x := make([]int, 10) 6 | go func() { 7 | x = make([]int, 20) 8 | c <- true 9 | }() 10 | _ = x[2:3] 11 | <-c 12 | } 13 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceSliceSlice2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool, 1) 5 | x := make([]int, 10) 6 | i := 2 7 | go func() { 8 | i = 3 9 | c <- true 10 | }() 11 | _ = x[i:4] 12 | <-c 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceSliceString/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool, 1) 5 | x := "hello" 6 | go func() { 7 | x = "world" 8 | c <- true 9 | }() 10 | _ = x[2:3] 11 | <-c 12 | } 13 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceSprint/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | var x int 7 | ch := make(chan bool, 1) 8 | go func() { 9 | fmt.Sprint(x) 10 | ch <- true 11 | }() 12 | x = 1 13 | <-ch 14 | } 15 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceStringPtrRW/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | ch := make(chan bool, 1) 5 | var x string 6 | p := &x 7 | go func() { 8 | *p = "a" 9 | ch <- true 10 | }() 11 | _ = *p 12 | <-ch 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceStringRW/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | ch := make(chan bool, 1) 5 | s := "" 6 | go func() { 7 | s = "abacaba" 8 | ch <- true 9 | }() 10 | _ = s 11 | <-ch 12 | } 13 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceStructFieldRW1/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Point struct { 4 | x, y int 5 | } 6 | 7 | type NamedPoint struct { 8 | name string 9 | p Point 10 | } 11 | 12 | func main() { 13 | p := Point{0, 0} 14 | ch := make(chan bool, 1) 15 | go func() { 16 | p.x = 1 17 | ch <- true 18 | }() 19 | _ = p.x 20 | <-ch 21 | } 22 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceStructFieldRW2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Point struct { 4 | x, y int 5 | } 6 | 7 | func main() { 8 | p := &Point{0, 0} 9 | ch := make(chan bool, 1) 10 | go func() { 11 | p.x = 1 12 | ch <- true 13 | }() 14 | _ = p.x 15 | <-ch 16 | } 17 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceStructFieldRW3/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Point struct { 4 | x, y int 5 | } 6 | 7 | type NamedPoint struct { 8 | name string 9 | p Point 10 | } 11 | 12 | func main() { 13 | p := NamedPoint{name: "a", p: Point{0, 0}} 14 | ch := make(chan bool, 1) 15 | go func() { 16 | p.p.x = 1 17 | ch <- true 18 | }() 19 | _ = p.p.x 20 | <-ch 21 | } 22 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceStructInd/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool, 1) 5 | type Item struct { 6 | x, y int 7 | } 8 | i := Item{} 9 | go func(p *Item) { 10 | *p = Item{} 11 | c <- true 12 | }(&i) 13 | i.y = 42 14 | <-c 15 | } 16 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceStructInit/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | type X struct { 5 | x, y int 6 | } 7 | c := make(chan bool, 1) 8 | y := 0 9 | go func() { 10 | y = 42 11 | c <- true 12 | }() 13 | x := X{x: y} 14 | _ = x 15 | <-c 16 | } 17 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceTLS/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | comm := make(chan *int) 5 | done := make(chan bool, 2) 6 | go func() { 7 | var x int 8 | comm <- &x 9 | x = 1 10 | x = *(<-comm) 11 | done <- true 12 | }() 13 | go func() { 14 | p := <-comm 15 | *p = 2 16 | comm <- p 17 | done <- true 18 | }() 19 | <-done 20 | <-done 21 | } 22 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceTypeAssert/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool, 1) 5 | x := 0 6 | var i interface{} = x 7 | go func() { 8 | y := 0 9 | i = y 10 | c <- true 11 | }() 12 | _ = i.(int) 13 | <-c 14 | } 15 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceUnaddressableMapLen/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | m := make(map[int]map[int]int) 5 | ch := make(chan int, 1) 6 | m[0] = make(map[int]int) 7 | go func() { 8 | _ = len(m[0]) 9 | ch <- 0 10 | }() 11 | m[0][0] = 1 12 | <-ch 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlib/TestRaceUnsafePtrRW/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "unsafe" 4 | 5 | func main() { 6 | var x, y, z int 7 | x, y, z = 1, 2, 3 8 | var p unsafe.Pointer = unsafe.Pointer(&x) 9 | ch := make(chan bool, 1) 10 | go func() { 11 | p = (unsafe.Pointer)(&z) 12 | ch <- true 13 | }() 14 | y = *(*int)(p) 15 | x = y 16 | <-ch 17 | } 18 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestNoRaceAnd/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool) 5 | x, y := 0, 0 6 | go func() { 7 | x = 1 8 | c <- true 9 | }() 10 | if y == 1 && x == 1 { 11 | } 12 | <-c 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestNoRaceBlank/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var a [5]int 5 | ch := make(chan bool, 1) 6 | go func() { 7 | _, _ = a[0], a[1] 8 | ch <- true 9 | }() 10 | _, _ = a[2], a[3] 11 | <-ch 12 | a[1] = a[0] 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestNoRaceCaseFallthrough/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x, y, z int 5 | _ = y 6 | ch := make(chan int, 2) 7 | z = 1 8 | 9 | go func() { 10 | y = x 11 | ch <- 1 12 | }() 13 | go func() { 14 | switch { 15 | case z == 1: 16 | case z == 2: 17 | x = 2 18 | } 19 | ch <- 1 20 | }() 21 | <-ch 22 | <-ch 23 | } 24 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestNoRaceEmptyStruct/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | type Empty struct{} 5 | type X struct { 6 | y int64 7 | Empty 8 | } 9 | type Y struct { 10 | x X 11 | y int64 12 | } 13 | c := make(chan X) 14 | var y Y 15 | go func() { 16 | x := y.x 17 | c <- x 18 | }() 19 | y.y = 42 20 | <-c 21 | } 22 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestNoRaceForIncr/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | done := make(chan bool) 5 | x := 0 6 | go func() { 7 | x++ 8 | done <- true 9 | }() 10 | for i := 0; i < 0; x++ { 11 | } 12 | <-done 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestNoRaceForInit/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | done := make(chan bool) 5 | c := make(chan bool) 6 | x := 0 7 | go func() { 8 | for { 9 | _, ok := <-c 10 | if !ok { 11 | done <- true 12 | return 13 | } 14 | x++ 15 | } 16 | }() 17 | i := 0 18 | for x = 42; i < 10; i++ { 19 | c <- true 20 | } 21 | close(c) 22 | <-done 23 | } 24 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestNoRaceFuncUnlock/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | func main() { 6 | ch := make(chan bool, 1) 7 | var mu sync.Mutex 8 | x := 0 9 | _ = x 10 | go func() { 11 | mu.Lock() 12 | x = 42 13 | mu.Unlock() 14 | ch <- true 15 | }() 16 | x = func(mu *sync.Mutex) int { 17 | mu.Lock() 18 | return 43 19 | }(&mu) 20 | mu.Unlock() 21 | <-ch 22 | } 23 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestNoRaceOr/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool) 5 | x, y := 0, 0 6 | go func() { 7 | x = 1 8 | c <- true 9 | }() 10 | if y == 0 || x == 1 { 11 | } 12 | <-c 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestNoRaceSelect1/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x int 5 | _ = x 6 | compl := make(chan bool) 7 | c := make(chan bool) 8 | c1 := make(chan bool) 9 | 10 | go func() { 11 | x = 1 12 | // At least two channels are needed because 13 | // otherwise the compiler optimizes select out. 14 | // See comment in runtime/select.go:^func selectgo. 15 | select { 16 | case c <- true: 17 | case c1 <- true: 18 | } 19 | compl <- true 20 | }() 21 | select { 22 | case <-c: 23 | case c1 <- true: 24 | } 25 | x = 2 26 | <-compl 27 | } 28 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestNoRaceSelect2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "runtime" 4 | 5 | func main() { 6 | var x int 7 | _ = x 8 | compl := make(chan bool) 9 | c := make(chan bool) 10 | c1 := make(chan bool) 11 | go func() { 12 | select { 13 | case <-c: 14 | case <-c1: 15 | } 16 | x = 1 17 | compl <- true 18 | }() 19 | x = 2 20 | close(c) 21 | runtime.Gosched() 22 | <-compl 23 | } 24 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestNoRaceSelect3/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var x int 5 | _ = x 6 | compl := make(chan bool) 7 | c := make(chan bool, 10) 8 | c1 := make(chan bool) 9 | go func() { 10 | x = 1 11 | select { 12 | case c <- true: 13 | case <-c1: 14 | } 15 | compl <- true 16 | }() 17 | <-c 18 | x = 2 19 | <-compl 20 | } 21 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestNoRaceSelect5/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "runtime" 4 | 5 | func main() { 6 | test := func(sel, needSched bool) { 7 | var x int 8 | _ = x 9 | ch := make(chan bool) 10 | c1 := make(chan bool) 11 | 12 | done := make(chan bool, 2) 13 | go func() { 14 | if needSched { 15 | runtime.Gosched() 16 | } 17 | // println(1) 18 | x = 1 19 | if sel { 20 | select { 21 | case ch <- true: 22 | case <-c1: 23 | } 24 | } else { 25 | ch <- true 26 | } 27 | done <- true 28 | }() 29 | 30 | go func() { 31 | // println(2) 32 | if sel { 33 | select { 34 | case <-ch: 35 | case <-c1: 36 | } 37 | } else { 38 | <-ch 39 | } 40 | x = 1 41 | done <- true 42 | }() 43 | <-done 44 | <-done 45 | } 46 | 47 | test(true, true) 48 | test(true, false) 49 | test(false, true) 50 | test(false, false) 51 | } 52 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestNoRaceShortCalc/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool) 5 | x, y := 0, 0 6 | go func() { 7 | y = 1 8 | c <- true 9 | }() 10 | if x == 0 || y == 0 { 11 | } 12 | <-c 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestNoRaceShortCalc2/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := make(chan bool) 5 | x, y := 0, 0 6 | go func() { 7 | y = 1 8 | c <- true 9 | }() 10 | if x == 1 && y == 0 { 11 | } 12 | <-c 13 | } 14 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestRaceAppendSliceStruct/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | type X struct { 5 | x, y int 6 | } 7 | c := make(chan bool, 1) 8 | x := make([]X, 10) 9 | go func() { 10 | y := make([]X, 0, 10) 11 | y = append(y, x...) 12 | c <- true 13 | }() 14 | x[1].y = 42 15 | <-c 16 | } 17 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestRaceHeapParam/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | done := make(chan bool) 5 | x := func() (x int) { 6 | go func() { 7 | x = 42 8 | done <- true 9 | }() 10 | return 11 | }() 12 | _ = x 13 | <-done 14 | } 15 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestRaceIndirection/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | ch := make(chan struct{}, 1) 5 | var y int 6 | var x *int = &y 7 | go func() { 8 | *x = 1 9 | ch <- struct{}{} 10 | }() 11 | *x = 2 12 | <-ch 13 | _ = *x 14 | } 15 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestRaceMethodThunk/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Base int 4 | 5 | func (b *Base) Foo() int { 6 | return 42 7 | } 8 | 9 | func (b Base) Bar() int { 10 | return int(b) 11 | } 12 | 13 | func main() { 14 | type Derived struct { 15 | pad int 16 | *Base 17 | } 18 | var d Derived 19 | done := make(chan bool) 20 | go func() { 21 | _ = d.Foo() 22 | done <- true 23 | }() 24 | d = Derived{} 25 | <-done 26 | } 27 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestRaceMethodThunk4/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Base int 4 | 5 | func (b *Base) Foo() int { 6 | return 42 7 | } 8 | 9 | func (b Base) Bar() int { 10 | return int(b) 11 | } 12 | 13 | func main() { 14 | type Derived struct { 15 | pad int 16 | *Base 17 | } 18 | var d Derived 19 | d.Base = new(Base) 20 | done := make(chan bool) 21 | go func() { 22 | _ = d.Bar() 23 | done <- true 24 | }() 25 | *(*int)(d.Base) = 42 26 | <-done 27 | } 28 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestRacePanic/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Point struct { 4 | x, y int 5 | } 6 | 7 | func main() { 8 | var x int 9 | _ = x 10 | var zero int = 0 11 | ch := make(chan bool, 2) 12 | go func() { 13 | defer func() { 14 | err := recover() 15 | if err == nil { 16 | panic("should be panicking") 17 | } 18 | x = 1 19 | ch <- true 20 | }() 21 | var y int = 1 / zero 22 | zero = y 23 | }() 24 | go func() { 25 | defer func() { 26 | err := recover() 27 | if err == nil { 28 | panic("should be panicking") 29 | } 30 | x = 2 31 | ch <- true 32 | }() 33 | var y int = 1 / zero 34 | zero = y 35 | }() 36 | 37 | <-ch 38 | <-ch 39 | if zero != 0 { 40 | panic("zero has changed") 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestRaceRange/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "runtime" 4 | 5 | func main() { 6 | const N = 2 7 | var a [N]int 8 | var x, y int 9 | _ = x + y 10 | done := make(chan bool, N) 11 | for i, v := range a { 12 | go func(i int) { 13 | // we don't want a write-vs-write race 14 | // so there is no array b here 15 | if i == 0 { 16 | x = v 17 | } else { 18 | y = v 19 | } 20 | done <- true 21 | }(i) 22 | // Ensure the goroutine runs before we continue the loop. 23 | runtime.Gosched() 24 | } 25 | for i := 0; i < N; i++ { 26 | <-done 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestRaceSliceStruct/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | type X struct { 5 | x, y int 6 | } 7 | c := make(chan bool, 1) 8 | x := make([]X, 10) 9 | go func() { 10 | y := make([]X, 10) 11 | copy(y, x) 12 | c <- true 13 | }() 14 | x[1].y = 42 15 | <-c 16 | } 17 | -------------------------------------------------------------------------------- /tests/testdata/stdlibNoSuccess/TestRaceStructRW/prog1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Point struct { 4 | x, y int 5 | } 6 | 7 | type NamedPoint struct { 8 | name string 9 | p Point 10 | } 11 | 12 | func main() { 13 | p := Point{0, 0} 14 | ch := make(chan bool, 1) 15 | go func() { 16 | p = Point{1, 1} 17 | ch <- true 18 | }() 19 | q := p 20 | <-ch 21 | p = q 22 | } 23 | -------------------------------------------------------------------------------- /utils/DoubleKeyMap.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "go/token" 5 | "hash/fnv" 6 | "strconv" 7 | ) 8 | 9 | type DoubleKeyMap map[uint32]struct{} 10 | 11 | func NewDoubleKeyMap() DoubleKeyMap { 12 | return make(DoubleKeyMap) 13 | } 14 | 15 | func getHash(num int) uint32 { 16 | h := fnv.New32a() 17 | _, _ = h.Write([]byte(strconv.Itoa(num))) 18 | return h.Sum32() 19 | } 20 | 21 | func calcHash(posA token.Pos, posB token.Pos) uint32 { 22 | hashA := getHash(int(posA)) 23 | hashB := getHash(int(posB)) 24 | return hashA + hashB 25 | } 26 | 27 | func (m DoubleKeyMap) Add(posA token.Pos, posB token.Pos) { 28 | key := calcHash(posA, posB) 29 | m[key] = struct{}{} // Hashes are added to make the dict commutative 30 | 31 | } 32 | func (m DoubleKeyMap) IsExist(posA token.Pos, posB token.Pos) bool { 33 | key := calcHash(posA, posB) 34 | _, ok := m[key] 35 | return ok 36 | } 37 | -------------------------------------------------------------------------------- /utils/counter.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | type Counter struct { 4 | count int 5 | } 6 | 7 | func NewCounter() *Counter { 8 | return &Counter{} 9 | } 10 | 11 | func (c *Counter) GetNext() int { 12 | c.count += 1 13 | return c.count 14 | } -------------------------------------------------------------------------------- /utils/set.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | type IntSet struct { 4 | set map[int]int 5 | } 6 | 7 | func (s *IntSet) Add(num int) bool { 8 | if !s.Exist(num) { 9 | s.set[num] = 1 10 | return true 11 | } 12 | return false 13 | } 14 | 15 | func (s *IntSet) Exist(num int) bool { 16 | _, exist := s.set[num] 17 | return exist 18 | } 19 | -------------------------------------------------------------------------------- /utils/stacks/basicBlockStack.go: -------------------------------------------------------------------------------- 1 | package stacks 2 | 3 | import "golang.org/x/tools/go/ssa" 4 | 5 | // Used by CFG to traverse the graph. It uses both as a stack for traversal by order, and as a map to count occurrences 6 | // and fast retrieval of items. 7 | 8 | type BlockMap map[int]struct{} 9 | 10 | func NewBlockMap() *BlockMap { 11 | blocksMap := make(BlockMap) 12 | return &blocksMap 13 | } 14 | 15 | func (s *BlockMap) Add(v *ssa.BasicBlock) { 16 | (*s)[v.Index] = struct{}{} 17 | } 18 | 19 | func (s *BlockMap) Remove(v *ssa.BasicBlock) { 20 | delete(*s, v.Index) 21 | } 22 | 23 | func (s *BlockMap) Contains(v int) bool { 24 | _, ok := (*s)[v] 25 | return ok 26 | } 27 | -------------------------------------------------------------------------------- /utils/stacks/callCommonStack.go: -------------------------------------------------------------------------------- 1 | package stacks 2 | 3 | import "golang.org/x/tools/go/ssa" 4 | 5 | type CallCommonStack []*ssa.CallCommon 6 | 7 | func NewCallCommonStack() *CallCommonStack { 8 | stack := make([]*ssa.CallCommon, 0) 9 | return (*CallCommonStack)(&stack) 10 | } 11 | 12 | func (s *CallCommonStack) GetItems() []*ssa.CallCommon { 13 | return *s 14 | } 15 | 16 | func (s *CallCommonStack) Push(v *ssa.CallCommon) { 17 | *s = append(*s, v) 18 | } 19 | 20 | func (s *CallCommonStack) Len() int { 21 | return len(*s) 22 | } 23 | 24 | func (s *CallCommonStack) Pop() *ssa.CallCommon { 25 | if len(*s) == 0 { 26 | return nil 27 | } 28 | v := (*s)[len(*s)-1] 29 | *s = (*s)[:len(*s)-1] 30 | return v 31 | } 32 | 33 | func (s *CallCommonStack) MergeStacks(ns *CallCommonStack) { 34 | for _, item := range ns.GetItems() { 35 | s.Push(item) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /utils/stacks/functionStack.go: -------------------------------------------------------------------------------- 1 | package stacks 2 | 3 | import "golang.org/x/tools/go/ssa" 4 | 5 | type FunctionStackWithMap struct { 6 | stack FunctionStack 7 | FunctionMap FunctionMap 8 | } 9 | 10 | func NewFunctionStackWithMap() *FunctionStackWithMap { 11 | stack := make([]*ssa.Function, 0) 12 | FunctionMap := make(FunctionMap) 13 | basicBlockStack := &FunctionStackWithMap{stack: stack, FunctionMap: FunctionMap} 14 | return basicBlockStack 15 | } 16 | 17 | func (s *FunctionStackWithMap) Copy() *FunctionStackWithMap { 18 | tmp := NewFunctionStackWithMap() 19 | tmpStack := s.stack.Copy() 20 | tmpMap := make(FunctionMap) 21 | for k, v := range s.FunctionMap { 22 | tmpMap[k] = v 23 | } 24 | tmp.stack = *tmpStack 25 | tmp.FunctionMap = tmpMap 26 | return tmp 27 | } 28 | 29 | func (s *FunctionStackWithMap) GetItems() *FunctionStack { 30 | return &s.stack 31 | } 32 | 33 | func (s *FunctionStackWithMap) Iter() []*ssa.Function { 34 | return s.stack 35 | } 36 | 37 | func (s *FunctionStackWithMap) Contains(v *ssa.Function) bool { 38 | _, ok := s.FunctionMap[v] 39 | return ok 40 | } 41 | 42 | func (s *FunctionStackWithMap) Push(v *ssa.Function) { 43 | s.stack.Push(v) 44 | s.FunctionMap[v] = struct{}{} 45 | } 46 | 47 | func (s *FunctionStackWithMap) Pop() *ssa.Function { 48 | v := s.stack.Pop() 49 | delete(s.FunctionMap, v) 50 | return v 51 | } 52 | 53 | func (s *FunctionStackWithMap) Merge(sn *FunctionStackWithMap) { 54 | for _, item := range sn.Iter() { 55 | s.stack.Push(item) 56 | s.FunctionMap[item] = struct{}{} 57 | } 58 | } 59 | 60 | type FunctionMap map[*ssa.Function]struct{} 61 | 62 | type FunctionStack []*ssa.Function 63 | 64 | func (s *FunctionStack) GetItems() []*ssa.Function { 65 | return *s 66 | } 67 | 68 | func (s *FunctionStack) Copy() *FunctionStack { 69 | tmp := make([]*ssa.Function, len(*s)) 70 | copy(tmp, *s) 71 | return (*FunctionStack)(&tmp) 72 | } 73 | 74 | func (s *FunctionStack) Push(v *ssa.Function) { 75 | *s = append(*s, v) 76 | } 77 | 78 | func (s *FunctionStack) Pop() *ssa.Function { 79 | if len(*s) == 0 { 80 | return nil 81 | } 82 | v := (*s)[len(*s)-1] 83 | *s = (*s)[:len(*s)-1] 84 | return v 85 | } 86 | 87 | func (s *FunctionStack) MergeStacks(items *FunctionStack) { 88 | for _, item := range items.GetItems() { 89 | s.Push(item) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /utils/stacks/intStack.go: -------------------------------------------------------------------------------- 1 | package stacks 2 | 3 | type IntStackWithMap struct { 4 | stack IntStack 5 | intMap IntMap 6 | } 7 | 8 | func NewEmptyIntStackWithMap() *IntStackWithMap { 9 | stack := make([]int, 0, 20) 10 | intMap := make(IntMap) 11 | basicBlockStack := &IntStackWithMap{stack: stack, intMap: intMap} 12 | return basicBlockStack 13 | } 14 | 15 | func NewIntStackWithMap(stack IntStack, intMap IntMap) *IntStackWithMap { 16 | basicBlockStack := &IntStackWithMap{stack: stack, intMap: intMap} 17 | return basicBlockStack 18 | } 19 | 20 | func (s *IntStackWithMap) Copy() *IntStackWithMap { 21 | tmp := &IntStackWithMap{} 22 | tmpStack := s.stack.Copy() 23 | tmpMap := make(IntMap, len(s.stack)) 24 | for k, v := range s.intMap { 25 | tmpMap[k] = v 26 | } 27 | tmp.stack = *tmpStack 28 | tmp.intMap = tmpMap 29 | return tmp 30 | } 31 | 32 | func (s *IntStackWithMap) GetItems() *IntStack { 33 | return &s.stack 34 | } 35 | 36 | func (s *IntStackWithMap) Iter() []int { 37 | return s.stack 38 | } 39 | 40 | func (s *IntStackWithMap) Contains(v int) bool { 41 | _, ok := s.intMap[v] 42 | return ok 43 | } 44 | 45 | func (s *IntStackWithMap) Push(v int) { 46 | s.stack.Push(v) 47 | s.intMap[v] = struct{}{} 48 | } 49 | 50 | func (s *IntStackWithMap) Pop() int { 51 | v := s.stack.Pop() 52 | delete(s.intMap, v) 53 | return v 54 | } 55 | 56 | func (s *IntStackWithMap) Merge(sn *IntStackWithMap) { 57 | for _, item := range sn.Iter() { 58 | s.stack.Push(item) 59 | } 60 | } 61 | 62 | type IntMap map[int]struct{} 63 | 64 | type IntStack []int 65 | 66 | func (s *IntStack) GetItems() []int { 67 | return *s 68 | } 69 | 70 | func (s *IntStack) Copy() *IntStack { 71 | tmp := make([]int, len(*s)) 72 | copy(tmp, *s) 73 | return (*IntStack)(&tmp) 74 | } 75 | 76 | func (s *IntStack) Push(v int) { 77 | *s = append(*s, v) 78 | } 79 | 80 | func (s *IntStack) Pop() int { 81 | if len(*s) == 0 { 82 | return -1 83 | } 84 | v := (*s)[len(*s)-1] 85 | *s = (*s)[:len(*s)-1] 86 | return v 87 | } 88 | 89 | func (s *IntStack) MergeStacks(items *IntStack) { 90 | for _, item := range items.GetItems() { 91 | s.Push(item) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /utils/util.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bufio" 5 | "github.com/stretchr/testify/require" 6 | "go/types" 7 | "golang.org/x/tools/go/ssa" 8 | "io/ioutil" 9 | "os" 10 | "testing" 11 | ) 12 | 13 | func IsCallTo(call *ssa.Function, names ...string) bool { 14 | fn, ok := call.Object().(*types.Func) 15 | if !ok { 16 | return false 17 | } 18 | q := fn.FullName() 19 | for _, name := range names { 20 | if q == name { 21 | return true 22 | } 23 | } 24 | return false 25 | } 26 | 27 | func OpenFile(fileName string) (*os.File, error) { 28 | f, err := os.Open(fileName) 29 | if err != nil { 30 | return nil, err 31 | } 32 | return f, nil 33 | } 34 | 35 | func CreateFile(fileName string) (*os.File, error) { 36 | f, err := os.Create(fileName) 37 | if err != nil { 38 | return nil, err 39 | } 40 | return f, nil 41 | } 42 | func WriteFile(f *os.File, text []byte) error { 43 | dataWriter := bufio.NewWriter(f) 44 | _, err := dataWriter.Write(text) 45 | if err != nil { 46 | return err 47 | } 48 | err = dataWriter.Flush() 49 | if err != nil { 50 | return err 51 | } 52 | return nil 53 | } 54 | 55 | func UpdateFile(t *testing.T, path string, data []byte) { 56 | f, err := CreateFile(path) 57 | require.NoError(t, err) 58 | err = WriteFile(f, data) 59 | require.NoError(t, err) 60 | err = f.Close() 61 | require.NoError(t, err) 62 | } 63 | 64 | func ReadFile(filePath string) ([]byte, error) { 65 | f, err := OpenFile(filePath) 66 | if err != nil { 67 | return nil, err 68 | } 69 | content, err := ioutil.ReadAll(f) 70 | if err != nil { 71 | return nil, err 72 | } 73 | err = f.Close() 74 | if err != nil { 75 | return nil, err 76 | } 77 | return content, err 78 | } 79 | 80 | func ReadLineByNumber(filePath string, lineNumber int) (string, error) { 81 | f, err := OpenFile(filePath) 82 | if err != nil { 83 | return "", err 84 | } 85 | 86 | scanner := bufio.NewScanner(f) 87 | line := "" 88 | for i := 1; scanner.Scan(); i++ { 89 | if i == lineNumber { 90 | line = scanner.Text() 91 | break 92 | } 93 | } 94 | 95 | err = f.Close() 96 | if err != nil { 97 | return "", err 98 | } 99 | return line, err 100 | } 101 | --------------------------------------------------------------------------------