Analysis of Go projects which is using Bazel

This blog post explains how to analyze projects written on Go programming language, how it works and how to analyze projects which are using Bazel build system.

What’s new

These changes will be available in the upcoming Svace 3.4 release.

Standard use case

When using standard Go build command go build the use case looks like this:

svace init
svace build [build command or build script]

At this stage, an intermediate representation will have been built for the entire project and its dependencies. In order to run the analysis you need to type svace analyze.

Let’s analyze the project Xray-core as an example. We have used commit 46d8bb5. Go version in go.mod is 1.20.

cd Xray-core
svace init
svace build go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main

The output to this is:

Now Svace will preprocess captured data.
Processing source code markup data...
[**********************************************************************] 100%
Assembled build object: [BUILD]9078355afe76cf71d39d75ae3e47737a240fb4c4

Now run the analysis on the project:

svace analyze

As the result we get a list of all the warnings found and the amount of said warnings:

...
Analysis results:
[BUFFER_OVERFLOW]
  DYNAMIC_OVERFLOW: 3
  OVERFLOW_UNDER_CHECK: 10
[DEAD_CODE]
  REDUNDANT_COMPARISON.ALWAYS_FALSE: 51
  UNREACHABLE_CODE: 479
  UNREACHABLE_CODE.RET: 29
[DEREF_OF_NULL]
  DEREF_AFTER_NULL: 9
  DEREF_AFTER_NULL.EX: 22
  DEREF_OF_NULL.ASSIGN: 2
  DEREF_OF_NULL.COND: 1
...
Total warnings: 4263
...

After this you are be able to see all the warnings using Svacer web-interface.

How it works

When using built-in go build build system Svace intercepts the call to go executable file and using go [build/install] arguments identifies all the required dependencies.

Analysis of Bazel projects

When using Bazel the build process of Go project is completely different. Firstly, Bazel doesn’t invoke go build directly, but compiles and links project using go tool [compile/link etc.]. Secondly, all the information about dependencies is passed via rules (go-rules) go_repository to be precise, which uses go mod download to install source code of required libraries into internal GOPATH of your project, which Bazel creates on its own. If you want to study Bazel more thoroughly, you can do so here.

We have supported basic project analysis for Bazel, which will be available in an upcoming Svace 3.4 release.

To analyze project with Bazel you have to run the following commands (otherwise some Go compilations may not be intercepted):

bazel clean --expunge   # clean up cache
bazel shutdown          # stop Bazel server processes

After that svace build must be ran with --enable-ptrace-all flag, or go tool [command] won’t be intercepted at all.

If you want only to analyze Go compilations, you have to add --disable-language all and --enable-language go flags, which will intercept and analyze only Go compilations. They can be used with svace build as well as with svace analyze.

For example, if the build command of your project is bazel build //..., then svace build will be:

svace build --disable-language all --enable-language go --enable-ptrace-all bazel build //...

In order to ensure Svace operates correctly, .svace-dir directory must be created in analyzed project’s root (this is only required for Bazel projects).

svace build will also work with bazelisk.

As a demonstration let’s analyze bazel-ethereum. Go version in go.mod is set to go 1.15:

cd bazel-go-ethereum
bazel clean --expunge
bazel shutdown

Run svace build:

svace init
svace build --enable-ptrace bazelisk build --spawn_strategy=local --define version="local" //... 

Run analysis:

svace analyze --disable-language all --enable-language go // C/C++ compilations were also intercepted

After the analysis ends, you will get a list of all the warnings found:

...
Analysis results:
[BUFFER_OVERFLOW]
  DYNAMIC_OVERFLOW: 3
  OVERFLOW_UNDER_CHECK: 10
[DEAD_CODE]
  REDUNDANT_COMPARISON.ALWAYS_FALSE: 51
  UNREACHABLE_CODE: 479
  UNREACHABLE_CODE.RET: 29
[DEREF_OF_NULL]
  DEREF_AFTER_NULL: 9
  DEREF_AFTER_NULL.EX: 22
  DEREF_OF_NULL.ASSIGN: 2
  DEREF_OF_NULL.COND: 1
...
Total warnings: 4263
...

After this you are be able to see all the warnings using Svacer web-interface.

How it works

Our build interception component monitors a low-level go tool compile command; in the module mode go mod download is also intercepted. Inside of .svace-dir/bitcode directory GOPATH (.gopath) is created. This GOPATH will be used in constructing intermediate representation, which is required for the analysis. All the dependencies which are given in go mod download via a go_repository rule will be downloaded in said directory in this format:

https://github.com/bazelbuild/bazel-gazelle/blob/master/repository.md#go_repository

# Download using "go mod download"
go_repository(
    name = "com_github_pkg_errors",
    importpath = "github.com/pkg/errors",
    sum = "h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=",
    version = "v0.8.1",
)

In other cases, the last version of a dependency will be analyzed, the source code of which is in Svace .gopath.


Varvara Dvortsova