# -*- mode: makefile -*-
# The first line sets the emacs major mode to Makefile
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
CBMC_STARTER_KIT_VERSION = CBMC starter kit 2.5
################################################################
# The CBMC Starter Kit depends on the files Makefile.common and
# run-cbmc-proofs.py. They are installed by the setup script
# cbmc-starter-kit-setup and updated to the latest version by the
# update script cbmc-starter-kit-update. For more information about
# the starter kit and these files and these scripts, see
# https://model-checking.github.io/cbmc-starter-kit
#
# Makefile.common implements what we consider to be some best
# practices for using cbmc for software verification.
#
# Section I gives default values for a large number of Makefile
# variables that control
# * how your code is built (include paths, etc),
# * what program transformations are applied to your code (loop
# unwinding, etc), and
# * what properties cbmc checks for in your code (memory safety, etc).
#
# These variables are defined below with definitions of the form
# VARIABLE ?= DEFAULT_VALUE
# meaning VARIABLE is set to DEFAULT_VALUE if VARIABLE has not already
# been given a value.
#
# For your project, you can override these default values with
# project-specific definitions in Makefile-project-defines.
#
# For any individual proof, you can override these default values and
# project-specific values with proof-specific definitions in the
# Makefile for your proof.
#
# The definitions in the proof Makefile override definitions in the
# project Makefile-project-defines which override definitions in this
# Makefile.common.
#
# Section II uses the values defined in Section I to build your code, run
# your proof, and build a report of your results. You should not need
# to modify or override anything in Section II, but you may want to
# read it to understand how the values defined in Section I control
# things.
#
# To use Makefile.common, set variables as described above as needed,
# and then for each proof,
#
# * Create a subdirectory
.
# * Write a proof harness (a function) with the name
# in a file with the name /.c
# * Write a makefile with the name /Makefile that looks
# something like
#
# HARNESS_FILE=
# HARNESS_ENTRY=
# PROOF_UID=
#
# PROJECT_SOURCES += $(SRCDIR)/libraries/api_1.c
# PROJECT_SOURCES += $(SRCDIR)/libraries/api_2.c
#
# PROOF_SOURCES += $(PROOFDIR)/harness.c
# PROOF_SOURCES += $(SRCDIR)/cbmc/proofs/stub_a.c
# PROOF_SOURCES += $(SRCDIR)/cbmc/proofs/stub_b.c
#
# UNWINDSET += foo.0:3
# UNWINDSET += bar.1:6
#
# REMOVE_FUNCTION_BODY += api_stub_a
# REMOVE_FUNCTION_BODY += api_stub_b
#
# DEFINES = -DDEBUG=0
#
# include ../Makefile.common
#
# * Change directory to and run make
#
# The proof setup script cbmc-starter-kit-setup-proof from the CBMC
# Starter Kit will do most of this for, creating a directory and
# writing a basic Makefile and proof harness into it that you can edit
# as described above.
#
# Warning: If you get results that are hard to explain, consider
# running "make clean" or "make veryclean" before "make" if you get
# results that are hard to explain. Dependency handling in this
# Makefile.common may not be perfect.
SHELL=/bin/bash
default: report
################################################################
################################################################
## Section I: This section gives common variable definitions.
##
## Override these definitions in Makefile-project-defines or
## your proof Makefile.
##
## Remember that Makefile.common and Makefile-project-defines are
## included into the proof Makefile in your proof directory, so all
## relative pathnames defined there should be relative to your proof
## directory.
################################################################
# Define the layout of the source tree and the proof subtree
#
# Generally speaking,
#
# SRCDIR = the root of the repository
# CBMC_ROOT = /srcdir/cbmc
# PROOF_ROOT = /srcdir/cbmc/proofs
# PROOF_SOURCE = /srcdir/cbmc/sources
# PROOF_INCLUDE = /srcdir/cbmc/include
# PROOF_STUB = /srcdir/cbmc/stubs
# PROOFDIR = the directory containing the Makefile for your proof
#
# The path /srcdir/cbmc used in the example above is determined by the
# setup script cbmc-starter-kit-setup. Projects usually create a cbmc
# directory somewhere in the source tree, and run the setup script in
# that directory. The value of CBMC_ROOT becomes the absolute path to
# that directory.
#
# The location of that cbmc directory in the source tree affects the
# definition of SRCDIR, which is defined in terms of the relative path
# from a proof directory to the repository root. The definition is
# usually determined by the setup script cbmc-starter-kit-setup and
# written to Makefile-template-defines, but you can override it for a
# project in Makefile-project-defines and for a specific proof in the
# Makefile for the proof.
# Absolute path to the directory containing this Makefile.common
# See https://ftp.gnu.org/old-gnu/Manuals/make-3.80/html_node/make_17.html
#
# Note: We compute the absolute paths to the makefiles in MAKEFILE_LIST
# before we filter the list of makefiles for %/Makefile.common.
# Otherwise an invocation of the form "make -f Makefile.common" will set
# MAKEFILE_LIST to "Makefile.common" which will fail to match the
# pattern %/Makefile.common.
#
MAKEFILE_PATHS = $(foreach makefile,$(MAKEFILE_LIST),$(abspath $(makefile)))
PROOF_ROOT = $(dir $(filter %/Makefile.common,$(MAKEFILE_PATHS)))
CBMC_ROOT = $(shell dirname $(PROOF_ROOT))
PROOF_SOURCE = $(CBMC_ROOT)/sources
PROOF_INCLUDE = $(CBMC_ROOT)/include
PROOF_STUB = $(CBMC_ROOT)/stubs
# Project-specific definitions to override default definitions below
# * Makefile-project-defines will never be overwritten
# * Makefile-template-defines may be overwritten when the starter
# kit is updated
sinclude $(PROOF_ROOT)/Makefile-project-defines
sinclude $(PROOF_ROOT)/Makefile-template-defines
# SRCDIR is the path to the root of the source tree
# This is a default definition that is frequently overridden in
# another Makefile, see the discussion of SRCDIR above.
SRCDIR ?= $(abspath ../..)
# PROOFDIR is the path to the directory containing the proof harness
PROOFDIR ?= $(abspath .)
################################################################
# Define how to run CBMC
# Do property checking with the external SAT solver given by
# EXTERNAL_SAT_SOLVER. Do coverage checking with the default solver,
# since coverage checking requires the use of an incremental solver.
# The EXTERNAL_SAT_SOLVER variable is typically set (if it is at all)
# as an environment variable or as a makefile variable in
# Makefile-project-defines.
#
# For a particular proof, if the default solver is faster, do property
# checking with the default solver by including this definition in the
# proof Makefile:
# USE_EXTERNAL_SAT_SOLVER =
#
ifneq ($(strip $(EXTERNAL_SAT_SOLVER)),)
USE_EXTERNAL_SAT_SOLVER ?= --external-sat-solver $(EXTERNAL_SAT_SOLVER)
endif
CHECKFLAGS += $(USE_EXTERNAL_SAT_SOLVER)
# Job pools
# For version of Litani that are new enough (where `litani print-capabilities`
# prints "pools"), proofs for which `EXPENSIVE = true` is set can be added to a
# "job pool" that restricts how many expensive proofs are run at a time. All
# other proofs will be built in parallel as usual.
#
# In more detail: all compilation, instrumentation, and report jobs are run with
# full parallelism as usual, even for expensive proofs. The CBMC jobs for
# non-expensive proofs are also run in parallel. The only difference is that the
# CBMC safety checks and coverage checks for expensive proofs are run with a
# restricted parallelism level. At any one time, only N of these jobs are run at
# once, amongst all the proofs.
#
# To configure N, Litani needs to be initialized with a pool called "expensive".
# For example, to only run two CBMC safety/coverage jobs at a time from amongst
# all the proofs, you would initialize litani like
# litani init --pools expensive:2
# The run-cbmc-proofs.py script takes care of this initialization through the
# --expensive-jobs-parallelism flag.
#
# To enable this feature, set
# the ENABLE_POOLS variable when running Make, like
# `make ENABLE_POOLS=true report`
# The run-cbmc-proofs.py script takes care of this through the
# --restrict-expensive-jobs flag.
ifeq ($(strip $(ENABLE_POOLS)),)
POOL =
else ifeq ($(strip $(EXPENSIVE)),)
POOL =
else
POOL = --pool expensive
endif
# Similar to the pool feature above. If Litani is new enough, enable
# profiling CBMC's memory use.
ifeq ($(strip $(ENABLE_MEMORY_PROFILING)),)
MEMORY_PROFILING =
else
MEMORY_PROFILING = --profile-memory
endif
# Property checking flags
#
# Each variable below controls a specific property checking flag
# within CBMC. If desired, a property flag can be disabled within
# a particular proof by nulling the corresponding variable. For
# instance, the following line:
#
# CHECK_FLAG_POINTER_CHECK =
#
# would disable the --pointer-check CBMC flag within:
# * an entire project when added to Makefile-project-defines
# * a specific proof when added to the harness Makefile
CBMC_FLAG_MALLOC_MAY_FAIL ?= --malloc-may-fail
CBMC_FLAG_MALLOC_FAIL_NULL ?= --malloc-fail-null
CBMC_FLAG_BOUNDS_CHECK ?= --bounds-check
CBMC_FLAG_CONVERSION_CHECK ?= --conversion-check
CBMC_FLAG_DIV_BY_ZERO_CHECK ?= --div-by-zero-check
CBMC_FLAG_FLOAT_OVERFLOW_CHECK ?= --float-overflow-check
CBMC_FLAG_NAN_CHECK ?= --nan-check
CBMC_FLAG_POINTER_CHECK ?= --pointer-check
CBMC_FLAG_POINTER_OVERFLOW_CHECK ?= --pointer-overflow-check
CBMC_FLAG_POINTER_PRIMITIVE_CHECK ?= --pointer-primitive-check
CBMC_FLAG_SIGNED_OVERFLOW_CHECK ?= --signed-overflow-check
CBMC_FLAG_UNDEFINED_SHIFT_CHECK ?= --undefined-shift-check
CBMC_FLAG_UNSIGNED_OVERFLOW_CHECK ?= --unsigned-overflow-check
CBMC_FLAG_UNWINDING_ASSERTIONS ?= --unwinding-assertions
CBMC_FLAG_UNWIND ?= --unwind 1
CBMC_FLAG_FLUSH ?= --flush
# CBMC flags used for property checking and coverage checking
CBMCFLAGS += $(CBMC_FLAG_UNWIND) $(CBMC_UNWINDSET) $(CBMC_FLAG_FLUSH)
# CBMC flags used for property checking
CHECKFLAGS += $(CBMC_FLAG_MALLOC_MAY_FAIL)
CHECKFLAGS += $(CBMC_FLAG_MALLOC_FAIL_NULL)
CHECKFLAGS += $(CBMC_FLAG_BOUNDS_CHECK)
CHECKFLAGS += $(CBMC_FLAG_CONVERSION_CHECK)
CHECKFLAGS += $(CBMC_FLAG_DIV_BY_ZERO_CHECK)
CHECKFLAGS += $(CBMC_FLAG_FLOAT_OVERFLOW_CHECK)
CHECKFLAGS += $(CBMC_FLAG_NAN_CHECK)
CHECKFLAGS += $(CBMC_FLAG_POINTER_CHECK)
CHECKFLAGS += $(CBMC_FLAG_POINTER_OVERFLOW_CHECK)
CHECKFLAGS += $(CBMC_FLAG_POINTER_PRIMITIVE_CHECK)
CHECKFLAGS += $(CBMC_FLAG_SIGNED_OVERFLOW_CHECK)
CHECKFLAGS += $(CBMC_FLAG_UNDEFINED_SHIFT_CHECK)
CHECKFLAGS += $(CBMC_FLAG_UNSIGNED_OVERFLOW_CHECK)
CHECKFLAGS += $(CBMC_FLAG_UNWINDING_ASSERTIONS)
# CBMC flags used for coverage checking
COVERFLAGS += $(CBMC_FLAG_MALLOC_MAY_FAIL)
COVERFLAGS += $(CBMC_FLAG_MALLOC_FAIL_NULL)
# Additional CBMC flag to CBMC control verbosity.
#
# Meaningful values are
# 0 none
# 1 only errors
# 2 + warnings
# 4 + results
# 6 + status/phase information
# 8 + statistical information
# 9 + progress information
# 10 + debug info
#
# Uncomment the following line or set in Makefile-project-defines
# CBMC_VERBOSITY ?= --verbosity 4
# Additional CBMC flag to control how CBMC treats static variables.
#
# NONDET_STATIC is a list of flags of the form --nondet-static
# and --nondet-static-exclude VAR. The --nondet-static flag causes
# CBMC to initialize static variables with unconstrained value
# (ignoring initializers and default zero-initialization). The
# --nondet-static-exclude VAR excludes VAR for the variables
# initialized with unconstrained values.
NONDET_STATIC ?=
# Flags to pass to goto-cc for compilation and linking
COMPILE_FLAGS ?= -Wall
LINK_FLAGS ?= -Wall
EXPORT_FILE_LOCAL_SYMBOLS ?= --export-file-local-symbols
# Preprocessor include paths -I...
INCLUDES ?=
# Preprocessor definitions -D...
DEFINES ?=
# CBMC object model
#
# CBMC_OBJECT_BITS is the number of bits in a pointer CBMC uses for
# the id of the object to which a pointer is pointing. CBMC uses 8
# bits for the object id by default. The remaining bits in the pointer
# are used for offset into the object. This limits the size of the
# objects that CBMC can model. This Makefile defines this bound on
# object size to be CBMC_MAX_OBJECT_SIZE. You are likely to get
# unexpected results if you try to malloc an object larger than this
# bound.
CBMC_OBJECT_BITS ?= 8
# CBMC loop unwinding (Normally set in the proof Makefile)
#
# UNWINDSET is a list of pairs of the form foo.1:4 meaning that
# CBMC should unwind loop 1 in function foo no more than 4 times.
# For historical reasons, the number 4 is one more than the number
# of times CBMC actually unwinds the loop.
UNWINDSET ?=
# CBMC early loop unwinding (Normally set in the proof Makefile)
#
# Most users can ignore this variable.
#
# This variable exists to support the use of loop and function
# contracts, two features under development for CBMC. Checking the
# assigns clause for function contracts and loop invariants currently
# assumes loop-free bodies for loops and functions with contracts
# (possibly after replacing nested loops with their own loop
# contracts). To satisfy this requirement, it may be necessary to
# unwind some loops before the function contract and loop invariant
# transformations are applied to the goto program. This variable
# EARLY_UNWINDSET is identical to UNWINDSET, and we assume that the
# loops mentioned in EARLY_UNWINDSET and UNWINDSET are disjoint.
EARLY_UNWINDSET ?=
# CBMC function removal (Normally set set in the proof Makefile)
#
# REMOVE_FUNCTION_BODY is a list of function names. CBMC will "undefine"
# the function, and CBMC will treat the function as having no side effects
# and returning an unconstrained value of the appropriate return type.
# The list should include the names of functions being stubbed out.
REMOVE_FUNCTION_BODY ?=
# CBMC function pointer restriction (Normally set in the proof Makefile)
#
# RESTRICT_FUNCTION_POINTER is a list of function pointer restriction
# instructions of the form:
#
# .function_pointer_call./[,]*
#
# The function pointer call number in the specified function gets
# rewritten to a case switch over a finite list of functions.
# If some possible target functions are omitted from the list a counter
# example trace will be found by CBMC, i.e. the transformation is sound.
# If the target functions are file-local symbols, then mangled names must
# be used.
RESTRICT_FUNCTION_POINTER ?=
# The project source files (Normally set set in the proof Makefile)
#
# PROJECT_SOURCES is the list of project source files to compile,
# including the source file defining the function under test.
PROJECT_SOURCES ?=
# The proof source files (Normally set in the proof Makefile)
#
# PROOF_SOURCES is the list of proof source files to compile, including
# the proof harness, and including any function stubs being used.
PROOF_SOURCES ?=
# The number of seconds that CBMC should be allowed to run for before
# being forcefully terminated. Currently, this is set to be less than
# the time limit for a CodeBuild job, which is eight hours. If a proof
# run takes longer than the time limit of the CI environment, the
# environment will halt the proof run without updating the Litani
# report, making the proof run appear to "hang".
CBMC_TIMEOUT ?= 21600
# Proof writers could add function contracts in their source code.
# These contracts are ignored by default, but may be enabled in two distinct
# contexts using the following two variables:
# 1. To check whether one or more function contracts are sound with respect to
# the function implementation, CHECK_FUNCTION_CONTRACTS should be a list of
# function names.
# 2. To replace calls to certain functions with their correspondent function
# contracts, USE_FUNCTION_CONTRACTS should be a list of function names.
# One must check separately whether a function contract is sound before
# replacing it in calling contexts.
CHECK_FUNCTION_CONTRACTS ?=
CBMC_CHECK_FUNCTION_CONTRACTS := $(patsubst %,--enforce-contract %, $(CHECK_FUNCTION_CONTRACTS))
USE_FUNCTION_CONTRACTS ?=
CBMC_USE_FUNCTION_CONTRACTS := $(patsubst %,--replace-call-with-contract %, $(USE_FUNCTION_CONTRACTS))
# Similarly, proof writers could also add loop contracts in their source code
# to obtain unbounded correctness proofs. Unlike function contracts, loop
# contracts are not reusable and thus are checked and used simultaneously.
# These contracts are also ignored by default, but may be enabled by setting
# the APPLY_LOOP_CONTRACTS variable to 1.
APPLY_LOOP_CONTRACTS ?= 0
ifeq ($(APPLY_LOOP_CONTRACTS),1)
CBMC_APPLY_LOOP_CONTRACTS ?= --apply-loop-contracts
endif
# Silence makefile output (eg, long litani commands) unless VERBOSE is set.
ifndef VERBOSE
MAKEFLAGS := $(MAKEFLAGS) -s
endif
################################################################
################################################################
## Section II: This section defines the process of running a proof
##
## There should be no reason to edit anything below this line.
################################################################
# Paths
CBMC ?= cbmc
GOTO_ANALYZER ?= goto-analyzer
GOTO_CC ?= goto-cc
GOTO_INSTRUMENT ?= goto-instrument
CRANGLER ?= crangler
VIEWER ?= cbmc-viewer
MAKE_SOURCE ?= make-source
VIEWER2 ?= cbmc-viewer
CMAKE ?= cmake
GOTODIR ?= $(PROOFDIR)/gotos
LOGDIR ?= $(PROOFDIR)/logs
PROJECT ?= project
PROOF ?= proof
HARNESS_GOTO ?= $(GOTODIR)/$(HARNESS_FILE)
PROJECT_GOTO ?= $(GOTODIR)/$(PROJECT)
PROOF_GOTO ?= $(GOTODIR)/$(PROOF)
################################################################
# Useful macros for values that are hard to reference
SPACE :=$() $()
COMMA :=,
################################################################
# Set C compiler defines
CBMCFLAGS += --object-bits $(CBMC_OBJECT_BITS)
COMPILE_FLAGS += --object-bits $(CBMC_OBJECT_BITS)
DEFINES += -DCBMC=1
DEFINES += -DCBMC_OBJECT_BITS=$(CBMC_OBJECT_BITS)
DEFINES += -DCBMC_MAX_OBJECT_SIZE="(SIZE_MAX>>(CBMC_OBJECT_BITS+1))"
# CI currently assumes cbmc invocation has at most one --unwindset
ifdef UNWINDSET
ifneq ($(strip $(UNWINDSET)),"")
CBMC_UNWINDSET := --unwindset $(subst $(SPACE),$(COMMA),$(strip $(UNWINDSET)))
endif
endif
ifdef EARLY_UNWINDSET
ifneq ($(strip $(EARLY_UNWINDSET)),"")
CBMC_EARLY_UNWINDSET := --unwindset $(subst $(SPACE),$(COMMA),$(strip $(EARLY_UNWINDSET)))
endif
endif
CBMC_REMOVE_FUNCTION_BODY := $(patsubst %,--remove-function-body %, $(REMOVE_FUNCTION_BODY))
CBMC_RESTRICT_FUNCTION_POINTER := $(patsubst %,--restrict-function-pointer %, $(RESTRICT_FUNCTION_POINTER))
################################################################
# Targets for rewriting source files with crangler
# Construct crangler configuration files
#
# REWRITTEN_SOURCES is a list of crangler output files source.i.
# This target assumes that for each source.i
# * source.i_SOURCE is the path to a source file,
# * source.i_FUNCTIONS is a list of functions (may be empty)
# * source.i_OBJECTS is a list of variables (may be empty)
# This target constructs the crangler configuration file source.i.json
# of the form
# {
# "sources": [ "/proj/code.c" ],
# "includes": [ "/proj/include" ],
# "defines": [ "VAR=1" ],
# "functions": [ {"function_name": ["remove static"]} ],
# "objects": [ {"variable_name": ["remove static"]} ],
# "output": "source.i"
# }
# to remove the static attribute from function_name and variable_name
# in the source file source.c and write the result to source.i.
#
# This target assumes that filenames include no spaces and that
# the INCLUDES and DEFINES variables include no spaces after -I
# and -D. For example, use "-DVAR=1" and not "-D VAR=1".
#
# Define *_SOURCE, *_FUNCTIONS, and *_OBJECTS in the proof Makefile.
# The string source.i is usually an absolute path $(PROOFDIR)/code.i
# to a file in the proof directory that contains the proof Makefile.
# The proof Makefile usually includes the definitions
# $(PROOFDIR)/code.i_SOURCE = /proj/code.c
# $(PROOFDIR)/code.i_FUNCTIONS = function_name
# $(PROOFDIR)/code.i_OBJECTS = variable_name
# Because these definitions refer to PROOFDIR that is defined in this
# Makefile.common, these definitions must appear after the inclusion
# of Makefile.common in the proof Makefile.
#
$(foreach rs,$(REWRITTEN_SOURCES),$(eval $(rs).json: $($(rs)_SOURCE)))
$(foreach rs,$(REWRITTEN_SOURCES),$(rs).json):
echo '{'\
'"sources": ['\
'"$($(@:.json=)_SOURCE)"'\
'],'\
'"includes": ['\
'$(subst $(SPACE),$(COMMA),$(patsubst -I%,"%",$(strip $(INCLUDES))))' \
'],'\
'"defines": ['\
'$(subst $(SPACE),$(COMMA),$(patsubst -D%,"%",$(subst ",\",$(strip $(DEFINES)))))' \
'],'\
'"functions": ['\
'{'\
'$(subst ~, ,$(subst $(SPACE),$(COMMA),$(patsubst %,"%":["remove~static"],$($(@:.json=)_FUNCTIONS))))' \
'}'\
'],'\
'"objects": ['\
'{'\
'$(subst ~, ,$(subst $(SPACE),$(COMMA),$(patsubst %,"%":["remove~static"],$($(@:.json=)_OBJECTS))))' \
'}'\
'],'\
'"output": "$(@:.json=)"'\
'}' > $@
# Rewrite source files with crangler
#
$(foreach rs,$(REWRITTEN_SOURCES),$(eval $(rs): $(rs).json))
$(REWRITTEN_SOURCES):
$(LITANI) add-job \
--command \
'$(CRANGLER) $@.json' \
--inputs $($@_SOURCE) \
--outputs $@ \
--stdout-file $(LOGDIR)/crangler-$(subst /,_,$(subst .,_,$@))-log.txt \
--interleave-stdout-stderr \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): removing static"
################################################################
# Build targets that make the relevant .goto files
# Compile project sources
$(PROJECT_GOTO)1.goto: $(PROJECT_SOURCES) $(REWRITTEN_SOURCES)
$(LITANI) add-job \
--command \
'$(GOTO_CC) $(CBMC_VERBOSITY) $(COMPILE_FLAGS) $(EXPORT_FILE_LOCAL_SYMBOLS) $(INCLUDES) $(DEFINES) $^ -o $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/project_sources-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): building project binary"
# Compile proof sources
$(PROOF_GOTO)1.goto: $(PROOF_SOURCES)
$(LITANI) add-job \
--command \
'$(GOTO_CC) $(CBMC_VERBOSITY) $(COMPILE_FLAGS) $(EXPORT_FILE_LOCAL_SYMBOLS) $(INCLUDES) $(DEFINES) $^ -o $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/proof_sources-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): building proof binary"
# Remove function bodies from project sources
$(PROJECT_GOTO)2.goto: $(PROJECT_GOTO)1.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(CBMC_REMOVE_FUNCTION_BODY) $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/remove_function_body-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): removing function bodies from project sources"
# Link project and proof sources into the proof harness
$(HARNESS_GOTO)1.goto: $(PROOF_GOTO)1.goto $(PROJECT_GOTO)2.goto
$(LITANI) add-job \
--command '$(GOTO_CC) $(CBMC_VERBOSITY) --function $(HARNESS_ENTRY) $^ $(LINK_FLAGS) -o $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/link_proof_project-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): linking project to proof"
# Restrict function pointers
$(HARNESS_GOTO)2.goto: $(HARNESS_GOTO)1.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(CBMC_RESTRICT_FUNCTION_POINTER) $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/restrict_function_pointer-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): restricting function pointers in project sources"
# Fill static variable with unconstrained values
$(HARNESS_GOTO)3.goto: $(HARNESS_GOTO)2.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(NONDET_STATIC) $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/nondet_static-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): setting static variables to nondet"
# Omit unused functions (sharpens coverage calculations)
$(HARNESS_GOTO)4.goto: $(HARNESS_GOTO)3.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) --drop-unused-functions $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/drop_unused_functions-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): dropping unused functions"
# Omit initialization of unused global variables (reduces problem size)
$(HARNESS_GOTO)5.goto: $(HARNESS_GOTO)4.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) --slice-global-inits $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/slice_global_inits-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): slicing global initializations"
# Replace function calls with function contracts
# This must be done before enforcing function contracts,
# since contract enforcement inlines all function calls.
$(HARNESS_GOTO)6.goto: $(HARNESS_GOTO)5.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(CBMC_USE_FUNCTION_CONTRACTS) $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/use_function_contracts-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): replacing function calls with function contracts"
# Unwind loops for loop and function contracts
$(HARNESS_GOTO)7.goto: $(HARNESS_GOTO)6.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(CBMC_EARLY_UNWINDSET) $(CBMC_FLAG_UNWINDING_ASSERTIONS) $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/unwind_loops-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): unwinding loops"
# Apply loop contracts
$(HARNESS_GOTO)8.goto: $(HARNESS_GOTO)7.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(CBMC_APPLY_LOOP_CONTRACTS) $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/apply_loop_contracts-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): applying loop contracts"
# Check function contracts
$(HARNESS_GOTO)9.goto: $(HARNESS_GOTO)8.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(CBMC_CHECK_FUNCTION_CONTRACTS) $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/check_function_contracts-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): checking function contracts"
# Final name for proof harness
$(HARNESS_GOTO).goto: $(HARNESS_GOTO)9.goto
$(LITANI) add-job \
--command 'cp $< $@' \
--inputs $^ \
--outputs $@ \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): copying final goto-binary"
################################################################
# Targets to run the analysis commands
$(LOGDIR)/result.txt: $(HARNESS_GOTO).goto
$(LITANI) add-job \
$(POOL) \
--command \
'$(CBMC) $(CBMC_VERBOSITY) $(CBMCFLAGS) $(CBMC_FLAG_UNWINDING_ASSERTIONS) $(CHECKFLAGS) --trace $<' \
--inputs $^ \
--outputs $@ \
--ci-stage test \
--stdout-file $@ \
$(MEMORY_PROFILING) \
--ignore-returns 10 \
--timeout $(CBMC_TIMEOUT) \
--pipeline-name "$(PROOF_UID)" \
--tags "stats-group:safety checks" \
--stderr-file $(LOGDIR)/result-err-log.txt \
--description "$(PROOF_UID): checking safety properties"
$(LOGDIR)/result.xml: $(HARNESS_GOTO).goto
$(LITANI) add-job \
$(POOL) \
--command \
'$(CBMC) $(CBMC_VERBOSITY) $(CBMCFLAGS) $(CBMC_FLAG_UNWINDING_ASSERTIONS) $(CHECKFLAGS) --trace --xml-ui $<' \
--inputs $^ \
--outputs $@ \
--ci-stage test \
--stdout-file $@ \
$(MEMORY_PROFILING) \
--ignore-returns 10 \
--timeout $(CBMC_TIMEOUT) \
--pipeline-name "$(PROOF_UID)" \
--tags "stats-group:safety checks" \
--stderr-file $(LOGDIR)/result-err-log.txt \
--description "$(PROOF_UID): checking safety properties"
$(LOGDIR)/property.xml: $(HARNESS_GOTO).goto
$(LITANI) add-job \
--command \
'$(CBMC) $(CBMC_VERBOSITY) $(CBMCFLAGS) $(CBMC_FLAG_UNWINDING_ASSERTIONS) $(CHECKFLAGS) --show-properties --xml-ui $<' \
--inputs $^ \
--outputs $@ \
--ci-stage test \
--stdout-file $@ \
--ignore-returns 10 \
--pipeline-name "$(PROOF_UID)" \
--stderr-file $(LOGDIR)/property-err-log.txt \
--description "$(PROOF_UID): printing safety properties"
$(LOGDIR)/coverage.xml: $(HARNESS_GOTO).goto
$(LITANI) add-job \
$(POOL) \
--command \
'$(CBMC) $(CBMC_VERBOSITY) $(CBMCFLAGS) $(COVERFLAGS) --cover location --xml-ui $<' \
--inputs $^ \
--outputs $@ \
--ci-stage test \
--stdout-file $@ \
$(MEMORY_PROFILING) \
--ignore-returns 10 \
--timeout $(CBMC_TIMEOUT) \
--pipeline-name "$(PROOF_UID)" \
--tags "stats-group:coverage computation" \
--stderr-file $(LOGDIR)/coverage-err-log.txt \
--description "$(PROOF_UID): calculating coverage"
define VIEWER_CMD
$(VIEWER) \
--result $(LOGDIR)/result.txt \
--block $(LOGDIR)/coverage.xml \
--property $(LOGDIR)/property.xml \
--srcdir $(SRCDIR) \
--goto $(HARNESS_GOTO).goto \
--htmldir $(PROOFDIR)/html
endef
export VIEWER_CMD
$(PROOFDIR)/html: $(LOGDIR)/result.txt $(LOGDIR)/property.xml $(LOGDIR)/coverage.xml
$(LITANI) add-job \
--command "$$VIEWER_CMD" \
--inputs $^ \
--outputs $(PROOFDIR)/html \
--pipeline-name "$(PROOF_UID)" \
--ci-stage report \
--stdout-file $(LOGDIR)/viewer-log.txt \
--description "$(PROOF_UID): generating report"
# Caution: run make-source before running property and coverage checking
# The current make-source script removes the goto binary
$(LOGDIR)/source.json:
mkdir -p $(dir $@)
$(RM) -r $(GOTODIR)
$(MAKE_SOURCE) --srcdir $(SRCDIR) --wkdir $(PROOFDIR) > $@
$(RM) -r $(GOTODIR)
define VIEWER2_CMD
$(VIEWER2) \
--result $(LOGDIR)/result.xml \
--coverage $(LOGDIR)/coverage.xml \
--property $(LOGDIR)/property.xml \
--srcdir $(SRCDIR) \
--goto $(HARNESS_GOTO).goto \
--reportdir $(PROOFDIR)/report \
--config $(PROOFDIR)/cbmc-viewer.json
endef
export VIEWER2_CMD
# Omit logs/source.json from report generation until make-sources
# works correctly with Makefiles that invoke the compiler with
# mutliple source files at once.
$(PROOFDIR)/report: $(LOGDIR)/result.xml $(LOGDIR)/property.xml $(LOGDIR)/coverage.xml
$(LITANI) add-job \
--command "$$VIEWER2_CMD" \
--inputs $^ \
--outputs $(PROOFDIR)/report \
--pipeline-name "$(PROOF_UID)" \
--stdout-file $(LOGDIR)/viewer-log.txt \
--ci-stage report \
--description "$(PROOF_UID): generating report"
litani-path:
@echo $(LITANI)
# ##############################################################
# Phony Rules
#
# These rules provide a convenient way to run a single proof up to a
# certain stage. Users can browse into a proof directory and run
# "make -Bj 3 report" to generate a report for just that proof, or
# "make goto" to build the goto binary. Under the hood, this runs litani
# for just that proof.
_goto: $(HARNESS_GOTO).goto
goto:
@ echo Running 'litani init'
$(LITANI) init --project $(PROJECT_NAME)
@ echo Running 'litani add-job'
$(MAKE) -B _goto
@ echo Running 'litani build'
$(LITANI) run-build
_result: $(LOGDIR)/result.txt
result:
@ echo Running 'litani init'
$(LITANI) init --project $(PROJECT_NAME)
@ echo Running 'litani add-job'
$(MAKE) -B _result
@ echo Running 'litani build'
$(LITANI) run-build
_property: $(LOGDIR)/property.xml
property:
@ echo Running 'litani init'
$(LITANI) init --project $(PROJECT_NAME)
@ echo Running 'litani add-job'
$(MAKE) -B _property
@ echo Running 'litani build'
$(LITANI) run-build
_coverage: $(LOGDIR)/coverage.xml
coverage:
@ echo Running 'litani init'
$(LITANI) init --project $(PROJECT_NAME)
@ echo Running 'litani add-job'
$(MAKE) -B _coverage
@ echo Running 'litani build'
$(LITANI) run-build
# Choose the invocation of cbmc-viewer depending on which version of
# cbmc-viewer is installed. The --version flag is not implemented in
# version 1 --- it is an "unrecognized argument" --- but it is
# implemented in version 2.
_report1: $(PROOFDIR)/html
_report2: $(PROOFDIR)/report
_report:
(cbmc-viewer --version 2>&1 | grep "unrecognized argument" > /dev/null) && \
$(MAKE) -B _report1 || $(MAKE) -B _report2
report report1 report2:
@ echo Running 'litani init'
$(LITANI) init --project $(PROJECT_NAME)
@ echo Running 'litani add-job'
$(MAKE) -B _report
@ echo Running 'litani build'
$(LITANI) run-build
################################################################
# Targets to clean up after ourselves
clean:
-$(RM) $(DEPENDENT_GOTOS)
-$(RM) TAGS*
-$(RM) *~ \#*
-$(RM) $(REWRITTEN_SOURCES) $(foreach rs,$(REWRITTEN_SOURCES),$(rs).json)
veryclean: clean
-$(RM) -r html report
-$(RM) -r $(LOGDIR) $(GOTODIR)
.PHONY: \
_coverage \
_goto \
_property \
_report \
_report2 \
_result \
clean \
coverage \
goto \
litani-path \
property \
report \
report2 \
result \
setup_dependencies \
testdeps \
veryclean \
#
################################################################
# Rule for generating cbmc-batch.yaml, used by the CI at
# https://github.com/awslabs/aws-batch-cbmc/
JOB_OS ?= ubuntu16
JOB_MEMORY ?= 32000
# Proofs that are expected to fail should set EXPECTED to
# "FAILED" in their Makefile. Values other than SUCCESSFUL
# or FAILED will cause a CI error.
EXPECTED ?= SUCCESSFUL
define yaml_encode_options
"$(shell echo $(1) | sed 's/ ,/ /g' | sed 's/ /;/g')"
endef
CI_FLAGS = $(CBMCFLAGS) $(CHECKFLAGS) $(COVERFLAGS)
cbmc-batch.yaml:
@$(RM) $@
@echo 'build_memory: $(JOB_MEMORY)' > $@
@echo 'cbmcflags: $(strip $(call yaml_encode_options,$(CI_FLAGS)))' >> $@
@echo 'coverage_memory: $(JOB_MEMORY)' >> $@
@echo 'expected: $(EXPECTED)' >> $@
@echo 'goto: $(HARNESS_GOTO).goto' >> $@
@echo 'jobos: $(JOB_OS)' >> $@
@echo 'property_memory: $(JOB_MEMORY)' >> $@
@echo 'report_memory: $(JOB_MEMORY)' >> $@
.PHONY: cbmc-batch.yaml
################################################################
# Run "make echo-proof-uid" to print the proof ID of a proof. This can be
# used by scripts to ensure that every proof has an ID, that there are
# no duplicates, etc.
.PHONY: echo-proof-uid
echo-proof-uid:
@echo $(PROOF_UID)
.PHONY: echo-project-name
echo-project-name:
@echo $(PROJECT_NAME)
################################################################
# Project-specific targets requiring values defined above
sinclude $(PROOF_ROOT)/Makefile-project-targets
# CI-specific targets to drive cbmc in CI
sinclude $(PROOF_ROOT)/Makefile-project-testing
################################################################