Skip to content
Snippets Groups Projects
Commit e2cab5c3 authored by Andi-Bogdan Postelnicu's avatar Andi-Bogdan Postelnicu
Browse files

Bug 1731542 - remove `infer` from our static-analysis pipeline. r=static-analysis-reviewers,marco

parent 0873ed43
No related branches found
No related tags found
No related merge requests found
Showing
with 5 additions and 1090 deletions
......@@ -50,7 +50,6 @@ exclude =
toolkit/nss.configure,
# These paths are intentionally excluded (not necessarily for good reason).
build/build-infer/build-infer.py,
build/moz.configure/*.configure,
build/pymake/,
browser/extensions/mortar/ppapi/,
......@@ -74,7 +73,6 @@ exclude =
testing/mozharness/configs/test/test_malformed.py,
testing/web-platform/tests,
tools/lint/test/files,
tools/infer/test/*.configure,
tools/crashreporter/*.configure,
.ycm_extra_conf.py,
......
......@@ -84,7 +84,6 @@ _OPT\.OBJ/
# Gradle cache.
^.gradle/
^tools/infer/test/.gradle/
# Local Gradle configuration properties.
^local.properties$
......@@ -192,9 +191,6 @@ tps_result\.json
# Ignore Visual Studio Code workspace files.
\.vscode/(?!extensions\.json|tasks\.json)
# Ignore Infer output
^infer-out/
# https://bz.mercurial-scm.org/show_bug.cgi?id=5322
^comm/
......
build-infer.py
==============
A script to build infer from source.
```
usage: build-infer.py [-h] -c CONFIG [--clean]
optional arguments:
-h, --help show this help message and exit
-c CONFIG, --config CONFIG
infer configuration file
--clean Clean the build directory
```
Pre-requisites
--------------
* Working build toolchain.
* ocam
* git
* autoconf
* libsqlite-dev
* CMake
* Ninja
* Python 2.7
Please use the latest available CMake for your platform to avoid surprises.
Config file format
------------------
build-clang.py accepts a JSON config format with the following fields:
* infer_revision: The infer revision to build.
* infer_repo: git repository for infer.
* patches: Optional list of patches to apply.
\ No newline at end of file
#!/usr/bin/env python3
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import subprocess
import json
import argparse
import sys
import shutil
from functools import reduce
def check_run(args, path):
print(" ".join(args) + " in " + path, file=sys.stderr)
subprocess.run(args, cwd=path, check=True)
def run_in(path, args, extra_env=None):
"""
Runs the given commands in the directory specified by <path>.
"""
env = dict(os.environ)
env.update(extra_env or {})
check_run(args, path)
subprocess.run(args, cwd=path)
def build_tar_package(tar, name, base, directories):
name = os.path.realpath(name)
run_in(
base,
[tar, "-c", "-a", "-f", name] + directories,
)
def is_git_repo(dir):
"""Check whether the given directory is a git repository."""
from subprocess import CalledProcessError
try:
check_run(["git", "rev-parse"], dir)
return True
except CalledProcessError:
return False
def git_clone(main_dir, url, clone_dir, commit):
"""
Clones the repository from <url> into <clone_dir>, and brings the
repository to the state of <commit>.
"""
run_in(main_dir, ["git", "clone", url, clone_dir])
run_in(clone_dir, ["git", "checkout", commit])
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"-c",
"--config",
required=True,
type=argparse.FileType("r"),
help="Infer configuration file",
)
parser.add_argument(
"-b", "--base-dir", help="Base directory for code and build artifacts"
)
parser.add_argument(
"--clean", action="store_true", help="Clean the build directory"
)
parser.add_argument(
"--skip-tar", action="store_true", help="Skip tar packaging stage"
)
args = parser.parse_args()
# The directories end up in the debug info, so the easy way of getting
# a reproducible build is to run it in a know absolute directory.
# We use a directory that is registered as a volume in the Docker image.
if args.base_dir:
base_dir = args.base_dir
else:
base_dir = reduce(
os.path.join, [os.sep + "builds", "worker", "workspace", "moz-toolchain"]
)
infer_dir = os.path.join(base_dir, "infer")
source_dir = os.path.join(infer_dir, "src")
build_dir = os.path.join(infer_dir, "build")
if args.clean:
shutil.rmtree(build_dir)
os.sys.exit(0)
config = json.load(args.config)
infer_revision = config["infer_revision"]
infer_repo = config["infer_repo"]
for folder in [infer_dir, source_dir, build_dir]:
os.makedirs(folder, exist_ok=True)
# clone infer
if not is_git_repo(source_dir):
# git doesn't like cloning into a non-empty folder. If src is not a git
# repo then just remove it in order to reclone
shutil.rmtree(source_dir)
git_clone(infer_dir, infer_repo, source_dir, infer_revision)
# apply a few patches
dir_path = os.path.dirname(os.path.realpath(__file__))
# clean the git directory by reseting all changes
git_commands = [["clean", "-f"], ["reset", "--hard"]]
for command in git_commands:
run_in(source_dir, ["git"] + command)
for p in config.get("patches", []):
run_in(source_dir, ["git", "apply", os.path.join(dir_path, p)])
# configure opam
run_in(source_dir, ["opam", "init", "--no-setup", "--disable-sandboxing"])
# build infer
run_in(source_dir, ["./build-infer.sh", "java"], extra_env={"NO_CMAKE_STRIP": "1"})
package_name = "infer"
infer_package = os.path.join(os.getcwd(), package_name)
# We need to create a package with all of the depended libraries injected in it
run_in(
source_dir,
[
"make",
"install-with-libs",
"BUILD_MODE=opt",
"PATCHELF=patchelf",
"DESTDIR={}".format(infer_package),
"libdir_relative_to_bindir=../lib",
],
)
infer_package_with_pref = os.path.join(infer_package, "usr")
if not args.skip_tar:
os.rename(
os.path.join(infer_package_with_pref, "local"),
os.path.join(infer_package_with_pref, "infer"),
)
build_tar_package(
"tar",
"%s.tar.zst" % (package_name),
infer_package_with_pref,
[
os.path.join("infer", "bin"),
os.path.join("infer", "lib"),
os.path.join("infer", "share"),
],
)
{
"infer_repo": "https://github.com/facebook/infer",
"infer_revision": "99464c01da5809e7159ed1a75ef10f60d34506a4",
"patches": []
}
This diff is collapsed.
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
---
job-defaults:
# Run only on try and code-review tasks
# to avoid running infer on the whole codebase
run-on-projects: []
platform: linux64/debug
worker-type: t-linux-xlarge-source
attributes:
code-review: true
worker:
docker-image: {in-tree: android-build}
max-run-time: 5400
treeherder:
kind: other
tier: 2
run:
using: run-task
tooltool-downloads: public
fetches:
toolchain:
- linux64-infer
- linux64-android-sdk-linux-repack
- linux64-android-ndk-linux-repack
- linux64-rust-android
- linux64-clang
- linux64-cbindgen
- linux64-nasm
- linux64-node
when:
files-changed:
- 'mobile/**/*.java'
infer:
description: Run static-analysis (infer) on Java patches
treeherder:
symbol: java(infer)
run:
cwd: '{checkout}'
command: >-
source taskcluster/scripts/misc/source-test-infer-setup.sh &&
./mach --log-no-times configure &&
./mach --log-no-times static-analysis check-java --outgoing --output $HOME/infer.json
worker:
artifacts:
- type: file
name: public/code-review/infer.json
path: /builds/worker/infer.json
......@@ -23,7 +23,6 @@ jobs-from:
- cram.yml
- doc.yml
- file-metadata.yml
- infer.yml
- jsshell.yml
- mozlint.yml
- mozlint-android.yml
......
......@@ -59,7 +59,6 @@ jobs:
toolchain:
- linux64-clang
- linux64-clang-tidy
- linux64-infer
- linux64-rust
- linux64-sccache
- linux64-cbindgen
......
......@@ -7,23 +7,6 @@ job-defaults:
worker:
max-run-time: 1800
linux64-infer:
description: "infer build"
index:
product: static-analysis
job-name: linux64-infer
treeherder:
symbol: TL(infer)
worker:
docker-image: {in-tree: static-analysis-build}
max-run-time: 3600
run:
script: build-infer-linux.sh
resources:
- 'build/build-infer/build-infer.py'
- 'build/build-infer/infer-linux64.json'
toolchain-artifact: public/build/infer.tar.zst
linux64-binutils-2.31.1:
description: "Binutils toolchain build"
treeherder:
......
#!/bin/bash
set -x -e -v
# This script is for building infer for Linux.
cd $GECKO_PATH
# gets a bit too verbose here
set +x
cd build/build-infer
./build-infer.py -c infer-linux64.json
set -x
# Put a tarball in the artifacts dir
mkdir -p $UPLOAD_DIR
cp infer.tar.* $UPLOAD_DIR
---
target: obj-x86_64-pc-linux-gnu
# It is used by 'mach static-analysis' and 'mozreview static-analysis bot'
# in order to have consistency across the used checkers.
platforms:
- linux64
infer_checkers:
# no issues were ever trigger by this
- name: check-nullable
publish: !!bool no
- name: biabduction
publish: !!bool yes
# very very noisy
# it could be useful, but it won't be part of the default enabled checkers
- name: eradicate
publish: !!bool no
# hard to use, not useful
- name: quandary
publish: !!bool no
- name: starvation
publish: !!bool yes
# experimental
- name: litho
publish: !!bool no
- name: racerd
publish: !!bool yes
# I think this is only for c++, can't trigger these errors in Java
- name: liveness
publish: !!bool no
# Third party files from mozilla-central
third_party: tools/rewriting/ThirdPartyPaths.txt
generated: tools/rewriting/Generated.txt
buildDir "${topobjdir}/gradle/build/tools/infer/test/autotest"
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile "com.google.code.findbugs:jsr305:3.0.2"
}
def createSingleTask = { name ->
task("compileInferTest${name}", type: JavaCompile) {
source = fileTree(dir: '.', include: "src/main/java/${name}.java")
classpath = project.configurations.compileClasspath
destinationDir = file("${topobjdir}/gradle/build/tools/infer/test/autotest")
}
}
createSingleTask('Biabduction')
createSingleTask('Checkers')
createSingleTask('Eradicate')
createSingleTask('Racerd')
createSingleTask('Starvation')
\ No newline at end of file
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import javax.annotation.Nullable;
import java.util.List;
public class Biabduction {
private String get() { return null; }
public void f1() {
get().length(); // error
}
public void f2() {
try {
get().length(); // error
} catch (NullPointerException e) {
}
}
}
[{"key": "Biabduction.java|f1|NULL_DEREFERENCE", "hash": "18467ae22b3a0dde943dc9dfc84d193a", "severity": "ERROR", "column": -1, "bug_type_hum": "Null Dereference", "node_key": "9afcdcc9d4253c36267a0d34b98c337d", "bug_type": "NULL_DEREFERENCE", "file": "autotest/src/main/java/Biabduction.java", "procedure_start_line": 11, "line": 12, "bug_trace": [{"line_number": 11, "filename": "autotest/src/main/java/Biabduction.java", "description": "start of procedure f1()", "column_number": -1, "level": 0}, {"line_number": 12, "filename": "autotest/src/main/java/Biabduction.java", "description": "", "column_number": -1, "level": 0}, {"line_number": 9, "filename": "autotest/src/main/java/Biabduction.java", "description": "start of procedure get()", "column_number": -1, "level": 1}, {"line_number": 9, "filename": "autotest/src/main/java/Biabduction.java", "description": "return from a call to String Biabduction.get()", "column_number": -1, "level": 1}, {"line_number": 12, "filename": "autotest/src/main/java/Biabduction.java", "description": "", "column_number": -1, "level": 0}], "procedure": "Biabduction.f1():void", "qualifier": "object returned by `get(this)` could be null and is dereferenced at line 12."}, {"key": "Biabduction.java|f2|NULL_DEREFERENCE", "hash": "d2f31f10d3c48ee63c61f52f4f83de4c", "severity": "ERROR", "column": -1, "bug_type_hum": "Null Dereference", "node_key": "9afcdcc9d4253c36267a0d34b98c337d", "bug_type": "NULL_DEREFERENCE", "file": "autotest/src/main/java/Biabduction.java", "procedure_start_line": 15, "line": 17, "bug_trace": [{"line_number": 15, "filename": "autotest/src/main/java/Biabduction.java", "description": "start of procedure f2()", "column_number": -1, "level": 0}, {"line_number": 17, "filename": "autotest/src/main/java/Biabduction.java", "description": "", "column_number": -1, "level": 0}, {"line_number": 9, "filename": "autotest/src/main/java/Biabduction.java", "description": "start of procedure get()", "column_number": -1, "level": 1}, {"line_number": 9, "filename": "autotest/src/main/java/Biabduction.java", "description": "return from a call to String Biabduction.get()", "column_number": -1, "level": 1}, {"line_number": 17, "filename": "autotest/src/main/java/Biabduction.java", "description": "", "column_number": -1, "level": 0}], "procedure": "Biabduction.f2():void", "qualifier": "object returned by `get(this)` could be null and is dereferenced at line 17."}]
\ No newline at end of file
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
public class Checkers {
public static void leak() {
try {
BufferedReader br = new BufferedReader(
new FileReader(new File("some.txt"))
);
} catch (Exception e) {
}
}
public static void error1() {
String str = null;
try {
int x = str.length(); // Error: even if exception is caught
} catch (NullPointerException e) {
}
}
public static void error2() {
String str = null;
int x = str.length(); // Error: not checking for null
}
}
[{"key": "Checkers.java|leak|RESOURCE_LEAK", "hash": "9c8b2bb1dbffb7893fc2ae9f60f83653", "severity": "ERROR", "column": -1, "bug_type_hum": "Resource Leak", "node_key": "3a2af627d5d1f10e1994f6259cf18e4c", "bug_type": "RESOURCE_LEAK", "file": "autotest/src/main/java/Checkers.java", "procedure_start_line": 10, "line": 12, "bug_trace": [{"line_number": 10, "filename": "autotest/src/main/java/Checkers.java", "description": "start of procedure leak()", "column_number": -1, "level": 0}, {"line_number": 12, "filename": "autotest/src/main/java/Checkers.java", "description": "", "column_number": -1, "level": 0}], "procedure": "Checkers.leak():void", "qualifier": "resource of type `java.io.FileReader` acquired by call to `new()` at line 12 is not released after line 12."}, {"key": "Checkers.java|error1|NULL_DEREFERENCE", "hash": "04ff79bfff8a231ff4cdb045a76641f0", "severity": "ERROR", "column": -1, "bug_type_hum": "Null Dereference", "node_key": "c281f77c6dae544ee5fb7d5e2bb35118", "bug_type": "NULL_DEREFERENCE", "file": "autotest/src/main/java/Checkers.java", "procedure_start_line": 20, "line": 23, "bug_trace": [{"line_number": 20, "filename": "autotest/src/main/java/Checkers.java", "description": "start of procedure error1()", "column_number": -1, "level": 0}, {"line_number": 21, "filename": "autotest/src/main/java/Checkers.java", "description": "", "column_number": -1, "level": 0}, {"line_number": 23, "filename": "autotest/src/main/java/Checkers.java", "description": "", "column_number": -1, "level": 0}], "procedure": "Checkers.error1():void", "qualifier": "object `str` last assigned on line 21 could be null and is dereferenced at line 23."}, {"key": "Checkers.java|error2|NULL_DEREFERENCE", "hash": "c4fe7d68fbb6b3ea84f233de6fd3add8", "severity": "ERROR", "column": -1, "bug_type_hum": "Null Dereference", "node_key": "c281f77c6dae544ee5fb7d5e2bb35118", "bug_type": "NULL_DEREFERENCE", "file": "autotest/src/main/java/Checkers.java", "procedure_start_line": 29, "line": 31, "bug_trace": [{"line_number": 29, "filename": "autotest/src/main/java/Checkers.java", "description": "start of procedure error2()", "column_number": -1, "level": 0}, {"line_number": 30, "filename": "autotest/src/main/java/Checkers.java", "description": "", "column_number": -1, "level": 0}, {"line_number": 31, "filename": "autotest/src/main/java/Checkers.java", "description": "", "column_number": -1, "level": 0}], "procedure": "Checkers.error2():void", "qualifier": "object `str` last assigned on line 30 could be null and is dereferenced at line 31."}]
\ No newline at end of file
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import javax.annotation.Nullable;
// Examples taken from the infer website.
public class Eradicate {
public String f; // Because it is not annoted with nullable -> can never be null!
public void field(@Nullable Eradicate x) {
x.f = "3"; // Error: Eradicate null field access
}
public void method(@Nullable Object x) {
String s = x.toString(); // Error: Eradicate null method call
}
public void filedNotNull(@Nullable String s) {
f = s; // Error: Eradicate field not nullable
}
public Eradicate() {} // Error: Eradicate field not initialized
public void str(Eradicate x) {
String s = x.toString();
}
public void callStr(@Nullable Eradicate x) {
str(x); // Error: Eradicate parameter not nullable
}
public String shouldNotReturnNullBecauseNotAnnotated() {
return null; // Error: Eradicate return not nullable
}
public void redundant() {
String s = new String("abc");
if (s != null) { // Error: Eradicate condition redundant
int n = s.length();
}
}
@Nullable
public static String someMethod() {
return ""; // Error: Eradicate return overannotated
}
}
class B extends Eradicate {
@Nullable public String shouldNotReturnNullBecauseNotAnnotated() {
return null; // Error: Eradicate inconsistent subclass return annotation
}
public void field(Eradicate x) {} // Error: Inconsistent subclass parameter annotation
}
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import javax.annotation.concurrent.ThreadSafe;
// Examples taken from the infer website.
@ThreadSafe
public class Racerd {
private int mTemperature;
public void makeDinner() {
boilWater();
}
private void boilWater() {
mTemperature = 100; //Error: unprotected write.
}
}
@ThreadSafe
class Account {
int mBalance = 0;
public void deposit(int amount) {
if (amount > 0) {
mBalance += amount; // Error: unsynchronized write
}
}
public int withdraw(int amount){
if (amount >= 0 && mBalance - amount >= 0) {
mBalance -= amount; // Error: unsynchronized write
return mBalance; // Error: unsynchronized read
} else {
return 0;
}
}
}
[{"key": "Racerd.java|deposit|THREAD_SAFETY_VIOLATION", "hash": "1ec753a9022eedd27443e3ed3da93e44", "severity": "WARNING", "column": -1, "bug_type_hum": "Thread Safety Violation", "access": "hJWmvgAAACsAAAAEAAAAEwAAAA+gsFwA/5IJImF1dG90ZXN0L3NyYy9tYWluL2phdmEvUmFjZXJkLmphdmFA", "bug_type": "THREAD_SAFETY_VIOLATION", "file": "autotest/src/main/java/Racerd.java", "procedure_start_line": 0, "line": 28, "bug_trace": [{"line_number": 28, "filename": "autotest/src/main/java/Racerd.java", "description": "access to `this.mBalance`", "column_number": -1, "level": 0}], "procedure": "Account.deposit(int):void", "qualifier": "Unprotected write. Non-private method `void Account.deposit(int)` writes to field `this.mBalance` outside of synchronization.\n Reporting because the current class is annotated `@ThreadSafe`, so we assume that this method can run in parallel with other non-private methods in the class (including itself)."}, {"key": "Racerd.java|makeDinner|THREAD_SAFETY_VIOLATION", "hash": "b4c48330cdd4742e24fdfc5c809ff604", "severity": "WARNING", "column": -1, "bug_type_hum": "Thread Safety Violation", "access": "hJWmvgAAACsAAAAEAAAAEwAAAA+gsFEA/5IJImF1dG90ZXN0L3NyYy9tYWluL2phdmEvUmFjZXJkLmphdmFA", "bug_type": "THREAD_SAFETY_VIOLATION", "file": "autotest/src/main/java/Racerd.java", "procedure_start_line": 0, "line": 13, "bug_trace": [{"line_number": 13, "filename": "autotest/src/main/java/Racerd.java", "description": "call to void Racerd.boilWater()", "column_number": -1, "level": 0}, {"line_number": 17, "filename": "autotest/src/main/java/Racerd.java", "description": "access to `this.mTemperature`", "column_number": -1, "level": 1}], "procedure": "Racerd.makeDinner():void", "qualifier": "Unprotected write. Non-private method `void Racerd.makeDinner()` indirectly writes to field `this.mTemperature` outside of synchronization.\n Reporting because the current class is annotated `@ThreadSafe`, so we assume that this method can run in parallel with other non-private methods in the class (including itself)."}, {"key": "Racerd.java|withdraw|THREAD_SAFETY_VIOLATION", "hash": "4cb9f0b0b4649ec0f84dea3541a6c70d", "severity": "WARNING", "column": -1, "bug_type_hum": "Thread Safety Violation", "access": "hJWmvgAAADIAAAAGAAAAGgAAABagsGEA/5IJImF1dG90ZXN0L3NyYy9tYWluL2phdmEvUmFjZXJkLmphdmGgsGIA/wQEQA==", "bug_type": "THREAD_SAFETY_VIOLATION", "file": "autotest/src/main/java/Racerd.java", "procedure_start_line": 0, "line": 33, "bug_trace": [{"line_number": 33, "filename": "autotest/src/main/java/Racerd.java", "description": "<Read trace>", "column_number": -1, "level": 0}, {"line_number": 33, "filename": "autotest/src/main/java/Racerd.java", "description": "access to `this.mBalance`", "column_number": -1, "level": 0}, {"line_number": 34, "filename": "autotest/src/main/java/Racerd.java", "description": "<Write trace>", "column_number": -1, "level": 0}, {"line_number": 34, "filename": "autotest/src/main/java/Racerd.java", "description": "access to `this.mBalance`", "column_number": -1, "level": 0}], "procedure": "Account.withdraw(int):int", "qualifier": "Read/Write race. Non-private method `int Account.withdraw(int)` reads without synchronization from `this.mBalance`. Potentially races with write in method `Account.withdraw(...)`.\n Reporting because the current class is annotated `@ThreadSafe`, so we assume that this method can run in parallel with other non-private methods in the class (including itself)."}, {"key": "Racerd.java|withdraw|THREAD_SAFETY_VIOLATION", "hash": "83037b7ad2aa337deba2dbe26e892440", "severity": "WARNING", "column": -1, "bug_type_hum": "Thread Safety Violation", "access": "hJWmvgAAACsAAAAEAAAAEwAAAA+gsGIA/5IJImF1dG90ZXN0L3NyYy9tYWluL2phdmEvUmFjZXJkLmphdmFA", "bug_type": "THREAD_SAFETY_VIOLATION", "file": "autotest/src/main/java/Racerd.java", "procedure_start_line": 0, "line": 34, "bug_trace": [{"line_number": 34, "filename": "autotest/src/main/java/Racerd.java", "description": "access to `this.mBalance`", "column_number": -1, "level": 0}], "procedure": "Account.withdraw(int):int", "qualifier": "Unprotected write. Non-private method `int Account.withdraw(int)` writes to field `this.mBalance` outside of synchronization.\n Reporting because the current class is annotated `@ThreadSafe`, so we assume that this method can run in parallel with other non-private methods in the class (including itself)."}]
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment