diff --git a/.travis.yml b/.travis.yml index f8ff1c6ac..3791b47ce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,18 +9,11 @@ addons: - cmake - python3-yaml - python3-psutil + - python3-pip - unzip - - libz-dev - - libedit-dev + - ninja-build - libboost-all-dev -cache: - directories: - - $HOME/build/smackers/boogie - - $HOME/build/smackers/corral - - $HOME/build/smackers/symbooglix - - $HOME/build/smackers/lockpwn - env: global: - COMPILER_NAME=clang COMPILER=clang++ CXX=clang++ CC=clang @@ -28,6 +21,7 @@ env: - TRAVIS_ENV="--exhaustive --folder=c/basic" - TRAVIS_ENV="--exhaustive --folder=c/data" - TRAVIS_ENV="--exhaustive --folder=c/ntdrivers-simplified" + - TRAVIS_ENV="--exhaustive --folder=c/ntdrivers" - TRAVIS_ENV="--exhaustive --folder=c/bits" - TRAVIS_ENV="--exhaustive --folder=c/float" - TRAVIS_ENV="--exhaustive --folder=c/locks" @@ -35,36 +29,41 @@ env: - TRAVIS_ENV="--exhaustive --folder=c/simd" - TRAVIS_ENV="--exhaustive --folder=c/memory-safety" - TRAVIS_ENV="--exhaustive --folder=c/pthread" + - TRAVIS_ENV="--exhaustive --folder=c/pthread_extras" - TRAVIS_ENV="--exhaustive --folder=c/strings" - TRAVIS_ENV="--exhaustive --folder=c/special" + - TRAVIS_ENV="--exhaustive --folder=rust/array --languages=rust" - TRAVIS_ENV="--exhaustive --folder=rust/basic --languages=rust" + - TRAVIS_ENV="--exhaustive --folder=rust/box --languages=rust" - TRAVIS_ENV="--exhaustive --folder=rust/functions --languages=rust" - TRAVIS_ENV="--exhaustive --folder=rust/generics --languages=rust" - TRAVIS_ENV="--exhaustive --folder=rust/loops --languages=rust" + - TRAVIS_ENV="--exhaustive --folder=rust/panic --languages=rust" - TRAVIS_ENV="--exhaustive --folder=rust/recursion --languages=rust" - TRAVIS_ENV="--exhaustive --folder=rust/structures --languages=rust" - TRAVIS_ENV="--exhaustive --folder=rust/vector --languages=rust" before_install: + - sudo rm -rf /usr/local/clang-7.0.0 + +install: + - source ./bin/versions - wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - - sudo add-apt-repository "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main" - - sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF + - sudo add-apt-repository "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-${LLVM_SHORT_VERSION} main" + - wget -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + - sudo dpkg -i packages-microsoft-prod.deb - sudo apt-get install -y apt-transport-https - - echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list - sudo apt-get update + - sudo apt-get install -y clang-${LLVM_SHORT_VERSION} clang-format-${LLVM_SHORT_VERSION} llvm-${LLVM_SHORT_VERSION}-dev dotnet-sdk-3.1 + - pip3 install -U flake8 + - sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${LLVM_SHORT_VERSION} 20 + - sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-${LLVM_SHORT_VERSION} 20 + - sudo update-alternatives --install /usr/bin/llvm-config llvm-config /usr/bin/llvm-config-${LLVM_SHORT_VERSION} 20 + - sudo update-alternatives --install /usr/bin/llvm-link llvm-link /usr/bin/llvm-link-${LLVM_SHORT_VERSION} 20 + - sudo update-alternatives --install /usr/bin/llvm-dis llvm-dis /usr/bin/llvm-dis-${LLVM_SHORT_VERSION} 20 + - sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-${LLVM_SHORT_VERSION} 20 -install: - - sudo apt-get install -y clang-8 clang-format-8 llvm-8-dev mono-complete ca-certificates-mono ninja-build - - sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-8 20 - - sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-8 20 - - sudo update-alternatives --install /usr/bin/llvm-config llvm-config /usr/bin/llvm-config-8 20 - - sudo update-alternatives --install /usr/bin/llvm-link llvm-link /usr/bin/llvm-link-8 20 - - sudo update-alternatives --install /usr/bin/llvm-dis llvm-dis /usr/bin/llvm-dis-8 20 - - sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-8 20 - - sudo pip install pyyaml psutil - -script: - - python --version +before_script: - python3 --version - $CXX --version - $CC --version @@ -72,5 +71,8 @@ script: - clang++ --version - llvm-link --version - llvm-config --version + +script: - ./format/run-clang-format.py -e test/c/basic/transform-out.c -r lib/smack include/smack share/smack/include share/smack/lib test examples + - flake8 test/regtest.py share/smack/ --extend-exclude share/smack/svcomp/,share/smack/reach.py - INSTALL_RUST=1 ./bin/build.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index c73d88a73..f6ea010dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,11 @@ cmake_minimum_required(VERSION 3.4.3) project(smack) if (NOT WIN32 OR MSYS OR CYGWIN) - find_program(LLVM_CONFIG_EXECUTABLE NAMES llvm-config-8 llvm-config PATHS ${LLVM_CONFIG} DOC "llvm-config") + + file(STRINGS "bin/versions" LLVM_VERSION_STR REGEX "LLVM_SHORT_VERSION=[0-9]+") + string(REGEX MATCH "[0-9]+" LLVM_SHORT_VERSION "${LLVM_VERSION_STR}") + + find_program(LLVM_CONFIG_EXECUTABLE NAMES llvm-config-${LLVM_SHORT_VERSION} llvm-config PATHS ${LLVM_CONFIG} DOC "llvm-config") if (LLVM_CONFIG_EXECUTABLE STREQUAL "LLVM_CONFIG_EXECUTABLE-NOTFOUND") message(FATAL_ERROR "llvm-config could not be found!") @@ -112,6 +116,7 @@ add_library(smackTranslator STATIC include/smack/BplFilePrinter.h include/smack/BplPrinter.h include/smack/DSAWrapper.h + include/smack/InitializePasses.h include/smack/Naming.h include/smack/Regions.h include/smack/SmackInstGenerator.h @@ -167,7 +172,7 @@ if (Boost_FOUND) endif () # We have to import LLVM's cmake definitions to build sea-dsa # Borrowed from sea-dsa's CMakeLists.txt -find_package (LLVM 8.0.1 EXACT CONFIG) +find_package (LLVM ${LLVM_SHORT_VERSION} CONFIG) list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") include(AddLLVM) include(HandleLLVMOptions) @@ -176,7 +181,7 @@ include(HandleLLVMOptions) # The following solution is from: https://stackoverflow.com/questions/30985215/passing-variables-down-to-subdirectory-only set(SMACK_BUILD_TYPE "${CMAKE_BUILD_TYPE}") set(CMAKE_BUILD_TYPE "Release") -add_subdirectory(sea-dsa/src) +add_subdirectory(sea-dsa/lib/seadsa) set(CMAKE_BUILD_TYPE ${SMACK_BUILD_TYPE}) target_link_libraries(smackTranslator ${LLVM_LIBS} ${LLVM_SYSTEM_LIBS} ${LLVM_LDFLAGS}) diff --git a/Doxyfile b/Doxyfile index 8f750c35f..a23d248f2 100644 --- a/Doxyfile +++ b/Doxyfile @@ -5,7 +5,7 @@ #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = smack -PROJECT_NUMBER = 2.4.1 +PROJECT_NUMBER = 2.5.0 PROJECT_BRIEF = "A bounded software verifier." PROJECT_LOGO = OUTPUT_DIRECTORY = docs diff --git a/LICENSE b/LICENSE index 833aa4e1d..28c461c44 100644 --- a/LICENSE +++ b/LICENSE @@ -1,8 +1,8 @@ The MIT License -Copyright (c) 2008-2019 Zvonimir Rakamaric (zvonimir@cs.utah.edu), +Copyright (c) 2008-2020 Zvonimir Rakamaric (zvonimir@cs.utah.edu), Michael Emmi (michael.emmi@gmail.com) -Modified work Copyright (c) 2013-2019 Marek Baranowski, +Modified work Copyright (c) 2013-2020 Marek Baranowski, Montgomery Carter, Pantazis Deligiannis, Jack J. Garzella, @@ -44,10 +44,7 @@ licenses, and/or restrictions: Program Directories License ------- ----------- ------- -poolalloc include/assistDS lib/DSA/LICENSE - include/dsa - lib/AssistDS - lib/DSA +sea-dsa sea-dsa sea-dsa/license.txt run-clang-format format format/LICENSE In addition, a binary distribution of SMACK contains at least the following diff --git a/README.md b/README.md index 912394b6d..03728e081 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ -| **master** | [![Build Status](https://travis-ci.com/smackers/smack.svg?branch=master)](https://travis-ci.com/smackers/smack) | **develop** | [![Build Status](https://travis-ci.com/smackers/smack.svg?branch=develop)](https://travis-ci.com/smackers/smack) | -| --- | --- | --- | --- | + +[![Build Status](https://travis-ci.com/smackers/smack.svg?branch=master)](https://travis-ci.com/smackers/smack) +[![Build Status](https://travis-ci.com/smackers/smack.svg?branch=develop)](https://travis-ci.com/smackers/smack) SMACK Logo @@ -47,10 +48,10 @@ See below for system requirements, installation, usage, and everything else. ### Acknowledgements -SMACK project is partially supported by NSF award CCF 1346756 and Microsoft -Research SEIF award. We also rely on University of Utah's -[Emulab](http://www.emulab.net/) infrastructure for extensive benchmarking of -SMACK. +SMACK project has been partially supported by funding from the National Science +Foundation, VMware, and Microsoft Research. We also rely on University of +Utah's [Emulab](http://www.emulab.net/) infrastructure for extensive +benchmarking of SMACK. ### Table of Contents diff --git a/Vagrantfile b/Vagrantfile index 7f342ebd1..bc66fc00b 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -25,7 +25,7 @@ Vagrant.configure(2) do |config| # opensuse_config.vm.box = "chef/opensuse-13.1" # end - config.vm.provision "shell", binary: true, inline: <<-SHELL + config.vm.provision "shell", binary: true, privileged: false, inline: <<-SHELL apt-get update apt-get install -y software-properties-common cd /home/vagrant diff --git a/bin/build.sh b/bin/build.sh index 1fc78fc7a..5751c9a3d 100755 --- a/bin/build.sh +++ b/bin/build.sh @@ -12,6 +12,7 @@ # - LLVM # - Clang # - Mono +# - Boost # - Z3 # - Boogie # - Corral @@ -22,16 +23,17 @@ # Set these flags to control various installation options INSTALL_DEPENDENCIES=1 +INSTALL_MONO=0 # Mono is needed only for lockpwn and symbooglix INSTALL_Z3=1 -INSTALL_CVC4=1 -BUILD_BOOGIE=1 -BUILD_CORRAL=1 -BUILD_SYMBOOGLIX=1 -BUILD_LOCKPWN=1 +INSTALL_CVC4=0 +INSTALL_YICES2=0 +INSTALL_BOOGIE=1 +INSTALL_CORRAL=1 +BUILD_SYMBOOGLIX=0 +BUILD_LOCKPWN=0 BUILD_SMACK=1 TEST_SMACK=1 BUILD_LLVM=0 # LLVM is typically installed from packages (see below) -BUILD_MONO=0 # mono is typically installed from packages (see below) # Support for more programming languages INSTALL_OBJECTIVEC=0 @@ -39,19 +41,20 @@ INSTALL_RUST=${INSTALL_RUST:-0} # PATHS SMACK_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )" -ROOT="$( cd "${SMACK_DIR}" && cd .. && pwd )" -Z3_DIR="${ROOT}/z3" -CVC4_DIR="${ROOT}/cvc4" -BOOGIE_DIR="${ROOT}/boogie" -CORRAL_DIR="${ROOT}/corral" -SYMBOOGLIX_DIR="${ROOT}/symbooglix" -LOCKPWN_DIR="${ROOT}/lockpwn" -MONO_DIR="${ROOT}/mono" -LLVM_DIR="${ROOT}/llvm" +ROOT_DIR="$( cd "${SMACK_DIR}" && cd .. && pwd )" +DEPS_DIR="${ROOT_DIR}/smack-deps" +Z3_DIR="${DEPS_DIR}/z3" +CVC4_DIR="${DEPS_DIR}/cvc4" +YICES2_DIR="${DEPS_DIR}/yices2" +BOOGIE_DIR="${DEPS_DIR}/boogie" +CORRAL_DIR="${DEPS_DIR}/corral" +SYMBOOGLIX_DIR="${DEPS_DIR}/symbooglix" +LOCKPWN_DIR="${DEPS_DIR}/lockpwn" +LLVM_DIR="${DEPS_DIR}/llvm" source ${SMACK_DIR}/bin/versions -SMACKENV=${ROOT}/smack.environment +SMACKENV=${ROOT_DIR}/smack.environment WGET="wget --no-verbose" # Install prefix -- system default is used if left unspecified @@ -60,7 +63,7 @@ CONFIGURE_INSTALL_PREFIX= CMAKE_INSTALL_PREFIX= # Partial list of dependencies; the rest are added depending on the platform -DEPENDENCIES="git cmake python3-yaml python3-psutil unzip wget ninja-build libboost-all-dev" +DEPENDENCIES="git cmake python3-yaml python3-psutil unzip wget ninja-build apt-transport-https dotnet-sdk-3.1 libboost-all-dev" shopt -s extglob @@ -183,43 +186,19 @@ puts "Detected distribution: $distro" # Set platform-dependent flags case "$distro" in linux-opensuse*) - Z3_DOWNLOAD_LINK="https://github.com/Z3Prover/z3/releases/download/Z3-${Z3_SHORT_VERSION}/z3-${Z3_FULL_VERSION}-x64-debian-8.10.zip" - DEPENDENCIES+=" llvm-clang llvm-devel gcc-c++ mono-complete ca-certificates-mono make" - DEPENDENCIES+=" ncurses-devel zlib-devel" - ;; - -linux-@(ubuntu|neon)-14*) - Z3_DOWNLOAD_LINK="https://github.com/Z3Prover/z3/releases/download/Z3-${Z3_SHORT_VERSION}/z3-${Z3_FULL_VERSION}-x64-ubuntu-14.04.zip" - DEPENDENCIES+=" clang-${LLVM_SHORT_VERSION} llvm-${LLVM_SHORT_VERSION}-dev mono-complete ca-certificates-mono libz-dev libedit-dev" + Z3_DOWNLOAD_LINK="https://github.com/Z3Prover/z3/releases/download/z3-${Z3_VERSION}/z3-${Z3_VERSION}-x64-debian-8.10.zip" + DEPENDENCIES+=" llvm-clang llvm-devel gcc-c++ make" + DEPENDENCIES+=" ncurses-devel" ;; linux-@(ubuntu|neon)-16*) - Z3_DOWNLOAD_LINK="https://github.com/Z3Prover/z3/releases/download/Z3-${Z3_SHORT_VERSION}/z3-${Z3_FULL_VERSION}-x64-ubuntu-16.04.zip" - DEPENDENCIES+=" clang-${LLVM_SHORT_VERSION} llvm-${LLVM_SHORT_VERSION}-dev mono-complete ca-certificates-mono libz-dev libedit-dev" + Z3_DOWNLOAD_LINK="https://github.com/Z3Prover/z3/releases/download/z3-${Z3_VERSION}/z3-${Z3_VERSION}-x64-ubuntu-16.04.zip" + DEPENDENCIES+=" clang-${LLVM_SHORT_VERSION} llvm-${LLVM_SHORT_VERSION}-dev" ;; linux-@(ubuntu|neon)-18*) - Z3_DOWNLOAD_LINK="https://github.com/Z3Prover/z3/releases/download/Z3-${Z3_SHORT_VERSION}/z3-${Z3_FULL_VERSION}-x64-ubuntu-16.04.zip" - DEPENDENCIES+=" clang-${LLVM_SHORT_VERSION} llvm-${LLVM_SHORT_VERSION}-dev mono-complete ca-certificates-mono libz-dev libedit-dev" - ;; - -linux-ubuntu-12*) - Z3_DOWNLOAD_LINK="https://github.com/Z3Prover/z3/releases/download/Z3-${Z3_SHORT_VERSION}/z3-${Z3_FULL_VERSION}-x64-ubuntu-14.04.zip" - DEPENDENCIES+=" g++-4.8 autoconf automake bison flex libtool gettext gdb" - DEPENDENCIES+=" libglib2.0-dev libfontconfig1-dev libfreetype6-dev libxrender-dev" - DEPENDENCIES+=" libtiff-dev libjpeg-dev libgif-dev libpng-dev libcairo2-dev" - BUILD_LLVM=1 - BUILD_MONO=1 - INSTALL_PREFIX="/usr/local" - CONFIGURE_INSTALL_PREFIX="--prefix=${INSTALL_PREFIX}" - CMAKE_INSTALL_PREFIX="-DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX}" - ;; - -linux-cygwin*) - BUILD_LLVM=1 - INSTALL_Z3=0 - BUILD_BOOGIE=0 - BUILD_CORRAL=0 + Z3_DOWNLOAD_LINK="https://github.com/Z3Prover/z3/releases/download/z3-${Z3_VERSION}/z3-${Z3_VERSION}-x64-ubuntu-16.04.zip" + DEPENDENCIES+=" clang-${LLVM_SHORT_VERSION} llvm-${LLVM_SHORT_VERSION}-dev" ;; *) @@ -258,12 +237,9 @@ if [ ${INSTALL_DEPENDENCIES} -eq 1 ] && [ "$TRAVIS" != "true" ] ; then sudo zypper --non-interactive install ${DEPENDENCIES} ;; - linux-@(ubuntu|neon)-1[468]*) + linux-@(ubuntu|neon)-1[68]*) RELEASE_VERSION=$(get-platform-trim "$(lsb_release -r)" | awk -F: '{print $2;}') case "$RELEASE_VERSION" in - 14*) - UBUNTU_CODENAME="trusty" - ;; 16*) UBUNTU_CODENAME="xenial" ;; @@ -283,11 +259,13 @@ if [ ${INSTALL_DEPENDENCIES} -eq 1 ] && [ "$TRAVIS" != "true" ] ; then # Adding LLVM repository ${WGET} -O - http://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo add-apt-repository "deb http://apt.llvm.org/${UBUNTU_CODENAME}/ llvm-toolchain-${UBUNTU_CODENAME}-${LLVM_SHORT_VERSION} main" - # Adding MONO repository - sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF - sudo apt-get install -y apt-transport-https - echo "deb https://download.mono-project.com/repo/ubuntu stable-${UBUNTU_CODENAME} main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list + + # Adding .NET repository + ${WGET} -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + sudo dpkg -i packages-microsoft-prod.deb + rm -f packages-microsoft-prod.deb sudo apt-get update + sudo apt-get install -y ${DEPENDENCIES} sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${LLVM_SHORT_VERSION} 30 sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-${LLVM_SHORT_VERSION} 30 @@ -296,20 +274,6 @@ if [ ${INSTALL_DEPENDENCIES} -eq 1 ] && [ "$TRAVIS" != "true" ] ; then sudo update-alternatives --install /usr/bin/llvm-dis llvm-dis /usr/bin/llvm-dis-${LLVM_SHORT_VERSION} 30 ;; - linux-ubuntu-12*) - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - sudo add-apt-repository -y ppa:andykimpe/cmake - sudo apt-get update - sudo apt-get install -y ${DEPENDENCIES} - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 20 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 20 - sudo update-alternatives --config gcc - sudo update-alternatives --config g++ - ;; - - linux-cygwin*) - ;; - *) puts "Distribution ${distro} not supported. Dependencies must be installed manually." exit 1 @@ -320,37 +284,19 @@ if [ ${INSTALL_DEPENDENCIES} -eq 1 ] && [ "$TRAVIS" != "true" ] ; then fi -if [ ${BUILD_MONO} -eq 1 ] ; then - puts "Building mono" - - git clone git://github.com/mono/mono.git ${MONO_DIR} - cd ${MONO_DIR} - git checkout mono-${MONO_VERSION} - ./autogen.sh ${CONFIGURE_INSTALL_PREFIX} - make get-monolite-latest - make EXTERNAL_MCS=${PWD}/mcs/class/lib/monolite/gmcs.exe - sudo make install - - # Install libgdiplus - cd ${MONO_DIR} - git clone git://github.com/mono/libgdiplus.git - cd libgdiplus - ./autogen.sh ${CONFIGURE_INSTALL_PREFIX} - make - sudo make install - - if [[ ${INSTALL_PREFIX} ]] ; then - echo export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${INSTALL_PREFIX}/lib >> ${SMACKENV} - source ${SMACKENV} - fi - - puts "Built mono" +if [ ${INSTALL_MONO} -eq 1 ] && [ "$TRAVIS" != "true" ] ; then + puts "Installing mono" + # Adding Mono repository + sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF + echo "deb https://download.mono-project.com/repo/ubuntu stable-${UBUNTU_CODENAME} main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list + sudo apt-get update + sudo apt-get install -y mono-complete ca-certificates-mono + puts "Installed mono" fi if [ ${BUILD_LLVM} -eq 1 ] ; then puts "Building LLVM" - mkdir -p ${LLVM_DIR}/src/{tools/clang,projects/compiler-rt} mkdir -p ${LLVM_DIR}/build @@ -366,34 +312,32 @@ if [ ${BUILD_LLVM} -eq 1 ] ; then cmake -G "Unix Makefiles" ${CMAKE_INSTALL_PREFIX} -DCMAKE_BUILD_TYPE=Release ../src make sudo make install - puts "Built LLVM" fi if [ ${INSTALL_OBJECTIVEC} -eq 1 ] ; then puts "Installing Objective-C" - # The version numbers here will have to change by OS sudo ln -s /usr/lib/gcc/x86_64-linux-gnu/4.8/include/objc /usr/local/include/objc echo ". /usr/share/GNUstep/Makefiles/GNUstep.sh" >> ${SMACKENV} - puts "Installed Objective-C" fi + if [ ${INSTALL_RUST} -eq 1 ] ; then puts "Installing Rust" - ${WGET} https://static.rust-lang.org/dist/${RUST_VERSION}/rust-nightly-x86_64-unknown-linux-gnu.tar.gz -O rust.tar.gz tar xf rust.tar.gz cd rust-nightly-x86_64-unknown-linux-gnu sudo ./install.sh --without=rust-docs cd .. - rm -r rust-nightly-x86_64-unknown-linux-gnu rust.tar.gz - + rm -rf rust-nightly-x86_64-unknown-linux-gnu rust.tar.gz + cargo install rustfilt puts "Installed Rust" fi + if [ ${INSTALL_Z3} -eq 1 ] ; then if [ ! -d "$Z3_DIR" ] ; then puts "Installing Z3" @@ -406,8 +350,10 @@ if [ ${INSTALL_Z3} -eq 1 ] ; then else puts "Z3 already installed" fi + echo export PATH=\"${Z3_DIR}/bin:\$PATH\" >> ${SMACKENV} fi + if [ ${INSTALL_CVC4} -eq 1 ] ; then if [ ! -d "$CVC4_DIR" ] ; then puts "Installing CVC4" @@ -418,52 +364,53 @@ if [ ${INSTALL_CVC4} -eq 1 ] ; then else puts "CVC4 already installed" fi + echo export PATH=\"${CVC4_DIR}:\$PATH\" >> ${SMACKENV} fi -if [ ${BUILD_BOOGIE} -eq 1 ] ; then - if ! upToDate $BOOGIE_DIR $BOOGIE_COMMIT ; then - puts "Building Boogie" - if [ ! -d "$BOOGIE_DIR/.git" ] ; then - git clone https://github.com/boogie-org/boogie.git ${BOOGIE_DIR} - fi - cd ${BOOGIE_DIR} - git reset --hard ${BOOGIE_COMMIT} - cd ${BOOGIE_DIR}/Source - ${WGET} https://dist.nuget.org/win-x86-commandline/latest/nuget.exe - mono ./nuget.exe restore Boogie.sln - rm -rf /tmp/nuget/ - msbuild Boogie.sln /p:Configuration=Release - ln -sf ${Z3_DIR}/bin/z3 ${BOOGIE_DIR}/Binaries/z3.exe - ln -sf ${CVC4_DIR}/cvc4 ${BOOGIE_DIR}/Binaries/cvc4.exe - puts "Built Boogie" + +if [ ${INSTALL_YICES2} -eq 1 ] ; then + if [ ! -d "$YICES2_DIR" ] ; then + puts "Installing Yices2" + mkdir -p ${YICES2_DIR} + ${WGET} https://yices.csl.sri.com/releases/${YICES2_VERSION}/yices-${YICES2_VERSION}-x86_64-pc-linux-gnu-static-gmp.tar.gz -O yices2-downloaded.tgz + tar xf yices2-downloaded.tgz + cd yices-${YICES2_VERSION} + ./install-yices ${YICES2_DIR} + cd .. + rm -rf yices2-downloaded.tgz yices-${YICES2_VERSION} + ln -s ${YICES2_DIR}/bin/yices-smt2 ${YICES2_DIR}/bin/yices2 + puts "Installed Yices2" else - puts "Boogie already built" + puts "Yices2 already installed" fi - echo export PATH=\"${BOOGIE_DIR}/Binaries:\$PATH\" >> ${SMACKENV} + echo export PATH=\"${YICES2_DIR}/bin:\$PATH\" >> ${SMACKENV} fi -if [ ${BUILD_CORRAL} -eq 1 ] ; then - if ! upToDate $CORRAL_DIR $CORRAL_COMMIT ; then - puts "Building Corral" - if [ ! -d "$CORRAL_DIR/.git" ] ; then - git clone https://github.com/boogie-org/corral.git ${CORRAL_DIR} - fi - cd ${CORRAL_DIR} - git reset --hard ${CORRAL_COMMIT} - git submodule init - git submodule update - msbuild cba.sln /p:Configuration=Release - ln -sf ${Z3_DIR}/bin/z3 ${CORRAL_DIR}/bin/Release/z3.exe - ln -sf ${CVC4_DIR}/cvc4 ${CORRAL_DIR}/bin/Release/cvc4.exe - sed -i.debug -e's/Debug/Release/' ${CORRAL_DIR}/bin/corral - puts "Built Corral" +if [ ${INSTALL_BOOGIE} -eq 1 ] ; then + if [ ! -d "$BOOGIE_DIR" ] ; then + puts "Installing Boogie" + dotnet tool install Boogie --tool-path ${BOOGIE_DIR} --version ${BOOGIE_VERSION} + puts "Installed Boogie" else - puts "Corral already built" + puts "Boogie already installed" fi - echo export PATH=\"${CORRAL_DIR}/bin:\$PATH\" >> ${SMACKENV} + echo export PATH=\"${BOOGIE_DIR}:\$PATH\" >> ${SMACKENV} fi + +if [ ${INSTALL_CORRAL} -eq 1 ] ; then + if [ ! -d "$CORRAL_DIR" ] ; then + puts "Installing Corral" + dotnet tool install Corral --tool-path ${CORRAL_DIR} --version ${CORRAL_VERSION} + puts "Installed Corral" + else + puts "Corral already installed" + fi + echo export PATH=\"${CORRAL_DIR}:\$PATH\" >> ${SMACKENV} +fi + + if [ ${BUILD_SYMBOOGLIX} -eq 1 ] ; then if ! upToDate $SYMBOOGLIX_DIR $SYMBOOGLIX_COMMIT ; then puts "Building Symbooglix" @@ -486,6 +433,7 @@ if [ ${BUILD_SYMBOOGLIX} -eq 1 ] ; then echo export PATH=\"${SYMBOOGLIX_DIR}/bin:\$PATH\" >> ${SMACKENV} fi + if [ ${BUILD_LOCKPWN} -eq 1 ] ; then if ! upToDate $LOCKPWN_DIR $LOCKPWN_COMMIT ; then puts "Building lockpwn" @@ -503,6 +451,7 @@ if [ ${BUILD_LOCKPWN} -eq 1 ] ; then echo export PATH=\"${LOCKPWN_DIR}/Binaries:\$PATH\" >> ${SMACKENV} fi + if [ ${BUILD_SMACK} -eq 1 ] ; then puts "Building SMACK" diff --git a/bin/versions b/bin/versions index 7436fd763..6be059a2c 100644 --- a/bin/versions +++ b/bin/versions @@ -1,11 +1,10 @@ -MONO_VERSION=5.0.0.100 -Z3_SHORT_VERSION=4.8.5 -Z3_FULL_VERSION=4.8.5 +Z3_VERSION=4.8.8 CVC4_VERSION=1.7 -BOOGIE_COMMIT=5c829b6340 -CORRAL_COMMIT=6437c41d34 +YICES2_VERSION=2.6.2 +BOOGIE_VERSION=2.6.15 +CORRAL_VERSION=1.0.12 SYMBOOGLIX_COMMIT=ccb2e7f2b3 LOCKPWN_COMMIT=12ba58f1ec -LLVM_SHORT_VERSION=8 -LLVM_FULL_VERSION=8.0.1 -RUST_VERSION=2019-07-16 +LLVM_SHORT_VERSION=9 +LLVM_FULL_VERSION=9.0.1 +RUST_VERSION=2020-05-21 diff --git a/docs/installation.md b/docs/installation.md index 9af8d42bf..7a7745d00 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -80,8 +80,8 @@ to Docker's official documentation. SMACK depends on the following projects: -* [LLVM][] version [8.0.1][LLVM-8.0.1] -* [Clang][] version [8.0.1][Clang-8.0.1] +* [LLVM][] version [9.0.1][LLVM-9.0.1] +* [Clang][] version [9.0.1][Clang-9.0.1] * [Boost][] version 1.55 or greater * [Python][] version 3.6.8 or greater * [Ninja][] version 1.5.1 or greater @@ -210,9 +210,9 @@ shell in the `test` directory by executing [CMake]: http://www.cmake.org [Python]: http://www.python.org [LLVM]: http://llvm.org -[LLVM-8.0.1]: http://llvm.org/releases/download.html#8.0.1 +[LLVM-9.0.1]: http://llvm.org/releases/download.html#9.0.1 [Clang]: http://clang.llvm.org -[Clang-8.0.1]: http://llvm.org/releases/download.html#8.0.1 +[Clang-9.0.1]: http://llvm.org/releases/download.html#9.0.1 [Boogie]: https://github.com/boogie-org/boogie [Corral]: https://github.com/boogie-org/corral [Z3]: https://github.com/Z3Prover/z3/ diff --git a/docs/projects.md b/docs/projects.md index 3eaaa2236..1bf6d8145 100644 --- a/docs/projects.md +++ b/docs/projects.md @@ -19,7 +19,7 @@ might have some expertise in Objective-C and/or Swift. And you could even base the project around verifying your own iPhone app's code. 3. Try to use and extend SMACK to verify some of the example from the -annual [Verified Software Competition (VSCOMP)](http://vscomp.org/). +annual [VerifyThis Competition](http://verifythis.ethz.ch). SMACK already has a rudimentary support for writing program contracts, but that would probably have to be extended. diff --git a/docs/publications.md b/docs/publications.md index 4a229f951..d43f5a5a7 100644 --- a/docs/publications.md +++ b/docs/publications.md @@ -4,7 +4,7 @@ ### Main Reference Please use the following publication when citing SMACK in your papers: -[SMACK: Decoupling Source Language Details from Verifier Implementations](http://soarlab.org/2014/05/smack-decoupling-source-language-details-from-verifier-implementations/), +[SMACK: Decoupling Source Language Details from Verifier Implementations](https://soarlab.org/publications/2014_cav_re), Zvonimir Rakamaric, Michael Emmi, 26th International Conference on Computer Aided Verification (CAV 2014) @@ -26,7 +26,7 @@ Bo-Yuan Huang, Sayak Ray, Aarti Gupta, Jason M. Fung, Sharad Malik, Dirk Beyer, Marie-Christine Jakobs, Thomas Lemberger, Heike Wehrheim, 40th International Conference on Software Engineering (ICSE 2018) -1. [ZEUS: Analyzing Safety of Smart Contracts](http://wp.internetsociety.org/ndss/wp-content/uploads/sites/25/2018/02/ndss2018_09-1_Kalra_paper.pdf), +1. [ZEUS: Analyzing Safety of Smart Contracts](https://www.ndss-symposium.org/wp-content/uploads/2018/02/ndss2018_09-1_Kalra_paper.pdf), Sukrit Kalra, Seep Goel, Mohan Dhawan, Subodh Sharma, 25th Annual Network and Distributed System Security Symposium (NDSS 2018) @@ -34,7 +34,7 @@ Sukrit Kalra, Seep Goel, Mohan Dhawan, Subodh Sharma, Yiji Zhang, Lenore D. Zuck, Keynote at the 14th International Conference on Distributed Computing and Internet Technology (ICDCIT 2018) -1. [Counterexample-Guided Bit-Precision Selection](http://soarlab.org/2017/09/aplas2017-hr/), +1. [Counterexample-Guided Bit-Precision Selection](https://soarlab.org/publications/2017_aplas_hr), Shaobo He, Zvonimir Rakamaric, 15th Asian Symposium on Programming Languages and Systems (APLAS 2017) @@ -42,7 +42,7 @@ Shaobo He, Zvonimir Rakamaric, Sunjay Cauligi, Gary Soeller, Fraser Brown, Brian Johannesmeyer, Yunlu Huang, Ranjit Jhala, Deian Stefan, IEEE Secure Development Conference (SecDev 2017) -1. [Static Assertion Checking of Production Software with Angelic Verification](http://soarlab.org/2017/09/tapas2017-hllr/), +1. [Static Assertion Checking of Production Software with Angelic Verification](https://soarlab.org/publications/2017_tapas_hllr), Shaobo He, Shuvendu Lahiri, Akash Lal, Zvonimir Rakamaric, 8th Workshop on Tools for Automatic Program Analysis (TAPAS 2017) @@ -51,7 +51,7 @@ Alex Gyori, Shuvendu Lahiri, Nimrod Partush, 26th ACM SIGSOFT International Symposium on Software Testing and Analysis (ISSTA 2017) -1. [System Programming in Rust: Beyond Safety](http://soarlab.org/2017/06/hotos2017-bbbprr/), +1. [System Programming in Rust: Beyond Safety](https://soarlab.org/publications/2017_hotos_bbbprr), Abhiram Balasubramanian, Marek S. Baranowski, Anton Burtsev, Aurojit Panda, Zvonimir Rakamaric, Leonid Ryzhyk, 16th Workshop on Hot Topics in Operating Systems (HotOS 2017) @@ -66,15 +66,15 @@ Yaniv David, Nimrod Partush, Eran Yahav, 37th ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI 2016) -1. [SMACK Software Verification Toolchain](http://soarlab.org/2016/02/icse2016-chwre/), +1. [SMACK Software Verification Toolchain](https://soarlab.org/publications/2016_icse_chwre), Montgomery Carter, Shaobo He, Jonathan Whitaker, Zvonimir Rakamaric, Michael Emmi, Demonstrations Track at the 38th IEEE/ACM International Conference on Software Engineering (ICSE 2016) -1. [Fast and Precise Symbolic Analysis of Concurrency Bugs in Device Drivers](http://soarlab.org/2015/08/ase2015-ddr/), +1. [Fast and Precise Symbolic Analysis of Concurrency Bugs in Device Drivers](https://soarlab.org/publications/2015_ase_ddr), Pantazis Deligiannis, Alastair F. Donaldson, Zvonimir Rakamaric, 30th IEEE/ACM International Conference on Automated Software Engineering (ASE 2015) -1. [SMACK+Corral: A Modular Verifier (Competition Contribution)](http://soarlab.org/2015/01/smackcorral-a-modular-verifier-competition-contribution/), +1. [SMACK+Corral: A Modular Verifier (Competition Contribution)](https://soarlab.org/publications/2015_tacas_hcelqr), Arvind Haran, Montgomery Carter, Michael Emmi, Akash Lal, Shaz Qadeer, Zvonimir Rakamaric, 21st International Conference on Tools and Algorithms for the Construction and Analysis of Systems (TACAS 2015) @@ -82,15 +82,15 @@ Arvind Haran, Montgomery Carter, Michael Emmi, Akash Lal, Shaz Qadeer, Zvonimir Pranav Garg, Christof Löding, P. Madhusudan, Daniel Neider, 26th International Conference on Computer Aided Verification (CAV 2014) -1. [Modular Verification of Shared-Memory Concurrent System Software](http://soarlab.org/2011/03/modular-verification-of-shared-memory-concurrent-system-software/), +1. [Modular Verification of Shared-Memory Concurrent System Software](https://soarlab.org/publications/2011_thesis_rakamaric), Zvonimir Rakamaric, Ph.D. Thesis, Department of Computer Science, University of British Columbia, 2011 -1. [A Scalable Memory Model for Low-Level Code](http://soarlab.org/2009/01/a-scalable-memory-model-for-low-level-code/), +1. [A Scalable Memory Model for Low-Level Code](https://soarlab.org/publications/2009_vmcai_rh), Zvonimir Rakamaric, Alan J. Hu, 10th International Conference on Verification, Model Checking and Abstract Interpretation (VMCAI 2009) -1. [Automatic Inference of Frame Axioms Using Static Analysis](http://soarlab.org/2008/09/automatic-inference-of-frame-axioms-using-static-analysis/), +1. [Automatic Inference of Frame Axioms Using Static Analysis](https://soarlab.org/publications/2008_ase_rh), Zvonimir Rakamaric, Alan J. Hu, 23rd IEEE/ACM International Conference on Automated Software Engineering (ASE 2008) diff --git a/docs/usage-notes.md b/docs/usage-notes.md index de6825971..246c697c5 100644 --- a/docs/usage-notes.md +++ b/docs/usage-notes.md @@ -32,11 +32,11 @@ of `x`, in the 3rd iteration) for the assertion to be reachable. ## Bitwise Operations and Integer Casts If the program to verify contains bitwise operations or integer casts, then the -flag `--bit-precise` may be required. The reason is that SMACK uses the SMT -theory of integers to encode machine integers by default, where bitwise -operations are encoded using uninterpreted functions returning arbitrary values. -Furthermore, precise encoding is required to handle integer signedness casts, -which is not also enabled automatically. +flag `--integer-encoding=bit-vector` may be required. The reason is that SMACK +uses the SMT theory of integers to encode machine integers by default, where +bitwise operations are encoded using uninterpreted functions returning +arbitrary values. Furthermore, precise encoding is required to handle integer +signedness casts, which is not also enabled automatically. The following program demonstrate the problems in the presence of bitwise operations. @@ -72,11 +72,11 @@ Some steps in the error trace are omitted. As you can see, the concrete values of `y` in the error trace before and after the bitwise right shift operation do not follow its semantics because it is modeled as an uninterpreted function. -In this case, we need the `--bit-precise` flag that, as its name indicates, -enables bit-precise modeling of machine integers. +In this case, we need the `--integer-encoding=bit-vector` flag that, as its +name indicates, enables bit-precise modeling of machine integers. ``` -$ smack a.c --bit-precise +$ smack a.c --integer-encoding=bit-vector SMACK program verifier version 1.9.0 SMACK found no errors with unroll bound 1. ``` @@ -91,11 +91,11 @@ Similar to machine integers, floating-point numbers and arithmetic are modeled using the theory of integers and uninterpreted functions, respectively. Therefore, if the assertions to verify depend on precise modeling of floating-point representations, the flag `--float` is needed. Note that -occasionally `--bit-precise` has to be used in addition to `--float`, in -particular when casts between floating-point numbers and integers are present. -Moreover, reasoning about floating-point numbers is often very slow. Please let -us know if you encounter any performance issues. We can share some experiences -that may ease the situation. +occasionally `--integer-encoding=bit-vector` has to be used in addition to +`--float`, in particular when casts between floating-point numbers and integers +are present. Moreover, reasoning about floating-point numbers is often very +slow. Please let us know if you encounter any performance issues. We can share +some experiences that may ease the situation. ## Concurrency Reasoning about pthreads is supported by SMACK with the flag `--pthread`. Please diff --git a/include/smack/CodifyStaticInits.h b/include/smack/CodifyStaticInits.h index 8a30c48dc..4ac92ae5e 100644 --- a/include/smack/CodifyStaticInits.h +++ b/include/smack/CodifyStaticInits.h @@ -20,4 +20,7 @@ class CodifyStaticInits : public llvm::ModulePass { virtual bool runOnModule(llvm::Module &M); virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const; }; + +llvm::Pass *createCodifyStaticInitsPass(); + } // namespace smack diff --git a/include/smack/DSAWrapper.h b/include/smack/DSAWrapper.h index 1e16f1c53..63f434d86 100644 --- a/include/smack/DSAWrapper.h +++ b/include/smack/DSAWrapper.h @@ -10,24 +10,24 @@ #include #include -#include "sea_dsa/DsaAnalysis.hh" -#include "sea_dsa/Global.hh" -#include "sea_dsa/Graph.hh" +#include "seadsa/DsaAnalysis.hh" +#include "seadsa/Global.hh" +#include "seadsa/Graph.hh" namespace smack { class DSAWrapper : public llvm::ModulePass { private: llvm::Module *module; - sea_dsa::GlobalAnalysis *SD; + seadsa::GlobalAnalysis *SD; // The ds graph since we're using the context-insensitive version which // results in one graph for the whole module. - sea_dsa::Graph *DG; - std::unordered_set staticInits; - std::unordered_set memOpds; + seadsa::Graph *DG; + std::unordered_set staticInits; + std::unordered_set memOpds; // Mapping from the DSNodes associated with globals to the numbers of // globals associated with them. - std::unordered_map globalRefCount; + std::unordered_map globalRefCount; const llvm::DataLayout *dataLayout; void collectStaticInits(llvm::Module &M); @@ -41,15 +41,15 @@ class DSAWrapper : public llvm::ModulePass { virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const; virtual bool runOnModule(llvm::Module &M); - bool isStaticInitd(const sea_dsa::Node *n); - bool isMemOpd(const sea_dsa::Node *n); + bool isStaticInitd(const seadsa::Node *n); + bool isMemOpd(const seadsa::Node *n); bool isRead(const llvm::Value *V); bool isSingletonGlobal(const llvm::Value *V); unsigned getPointedTypeSize(const llvm::Value *v); unsigned getOffset(const llvm::Value *v); - const sea_dsa::Node *getNode(const llvm::Value *v); + const seadsa::Node *getNode(const llvm::Value *v); bool isTypeSafe(const llvm::Value *v); - unsigned getNumGlobals(const sea_dsa::Node *n); + unsigned getNumGlobals(const seadsa::Node *n); }; } // namespace smack diff --git a/include/smack/InitializePasses.h b/include/smack/InitializePasses.h new file mode 100644 index 000000000..843d18ed7 --- /dev/null +++ b/include/smack/InitializePasses.h @@ -0,0 +1,14 @@ +// +// This file is distributed under the MIT License. See LICENSE for details. +// +#ifndef INITIALIZEPASSES_H +#define INITIALIZEPASSES_H + +#include "llvm/InitializePasses.h" + +namespace llvm { +void initializeDSAWrapperPass(PassRegistry &Registry); +void initializeCodifyStaticInitsPass(PassRegistry &Registry); +} // end namespace llvm + +#endif // INITIALIZEPASSES_H diff --git a/include/smack/MemorySafetyChecker.h b/include/smack/MemorySafetyChecker.h index f692a80ad..dd1a64d41 100644 --- a/include/smack/MemorySafetyChecker.h +++ b/include/smack/MemorySafetyChecker.h @@ -14,9 +14,6 @@ namespace smack { class MemorySafetyChecker : public llvm::FunctionPass, public llvm::InstVisitor { private: - std::map leakCheckFunction; - std::map safetyCheckFunction; - llvm::Function *getLeakCheckFunction(llvm::Module &M); llvm::Function *getSafetyCheckFunction(llvm::Module &M); diff --git a/include/smack/Naming.h b/include/smack/Naming.h index c41fa7103..6e19318d3 100644 --- a/include/smack/Naming.h +++ b/include/smack/Naming.h @@ -8,6 +8,7 @@ #include "llvm/IR/Value.h" #include "llvm/Support/Regex.h" #include +#include namespace smack { @@ -92,10 +93,11 @@ class Naming { static const std::string MEMORY_LEAK_FUNCTION; static const std::string RUST_ENTRY; - static const std::string RUST_PANIC1; - static const std::string RUST_PANIC2; + static const std::vector RUST_PANICS; static const std::string RUST_PANIC_ANNOTATION; + static const std::string INT_WRAP_SIGNED_FUNCTION; + static const std::string INT_WRAP_UNSIGNED_FUNCTION; static const std::map INSTRUCTION_TABLE; static const std::map CMPINST_TABLE; static const std::map ATOMICRMWINST_TABLE; @@ -110,6 +112,7 @@ class Naming { std::string freshBlockName(); std::string freshUndefName(); std::string freshVarName(const Value &V); + static std::string getIntWrapFunc(bool isUnsigned); static bool isBplKeyword(std::string s); static bool isSmackName(std::string s); diff --git a/include/smack/Regions.h b/include/smack/Regions.h index efc1903b9..d2c1ad0ea 100644 --- a/include/smack/Regions.h +++ b/include/smack/Regions.h @@ -4,7 +4,7 @@ #ifndef REGIONS_H #define REGIONS_H -#include "sea_dsa/Graph.hh" +#include "seadsa/Graph.hh" #include "llvm/IR/InstVisitor.h" using namespace llvm; @@ -20,7 +20,7 @@ class DSAWrapper; class Region { private: LLVMContext *context; - const sea_dsa::Node *representative; + const seadsa::Node *representative; const Type *type; unsigned offset; unsigned length; @@ -36,8 +36,8 @@ class Region { static DSAWrapper *DSA; static bool isSingleton(const llvm::Value *v, unsigned length); - static bool isAllocated(const sea_dsa::Node *N); - static bool isComplicated(const sea_dsa::Node *N); + static bool isAllocated(const seadsa::Node *N); + static bool isComplicated(const seadsa::Node *N); void init(const Value *V, unsigned length); bool isDisjoint(unsigned offset, unsigned length); diff --git a/include/smack/SmackOptions.h b/include/smack/SmackOptions.h index a6c210311..15aa72a7e 100644 --- a/include/smack/SmackOptions.h +++ b/include/smack/SmackOptions.h @@ -31,7 +31,9 @@ class SmackOptions { static const llvm::cl::opt MemorySafety; static const llvm::cl::opt IntegerOverflow; static const llvm::cl::opt LLVMAssumes; + static const llvm::cl::opt RustPanics; static const llvm::cl::opt AddTiming; + static const llvm::cl::opt WrappedIntegerEncoding; static bool isEntryPoint(std::string); }; diff --git a/include/smack/SmackRep.h b/include/smack/SmackRep.h index 2604150e4..875a6ad90 100644 --- a/include/smack/SmackRep.h +++ b/include/smack/SmackRep.h @@ -87,6 +87,8 @@ class SmackRep { const llvm::Value *rhs, const llvm::Type *t); const Expr *cmp(unsigned predicate, const llvm::Value *lhs, const llvm::Value *rhs, bool isUnsigned); + const Expr *select(const llvm::Value *condVal, const llvm::Value *trueVal, + const llvm::Value *falseVal); std::string procName(const llvm::User &U); std::string procName(llvm::Function *F, const llvm::User &U); @@ -148,6 +150,9 @@ class SmackRep { const Expr *cmp(const llvm::CmpInst *I); const Expr *cmp(const llvm::ConstantExpr *CE); + const Expr *select(const llvm::SelectInst *I); + const Expr *select(const llvm::ConstantExpr *CE); + const Expr *arg(llvm::Function *f, unsigned pos, llvm::Value *v); const Stmt *call(llvm::Function *f, const llvm::User &u); std::string code(llvm::CallInst &ci); diff --git a/include/utils/Devirt.h b/include/utils/Devirt.h index 1aad10057..2514c532c 100644 --- a/include/utils/Devirt.h +++ b/include/utils/Devirt.h @@ -23,7 +23,7 @@ #include "llvm/IR/InstVisitor.h" #include "llvm/IR/DataLayout.h" -#include "sea_dsa/CompleteCallGraph.hh" +#include "seadsa/CompleteCallGraph.hh" #include @@ -41,7 +41,7 @@ namespace llvm { class Devirtualize : public ModulePass, public InstVisitor { private: // Access to analysis pass which finds targets of indirect function calls - sea_dsa::CompleteCallGraph *CCG; + seadsa::CompleteCallGraph *CCG; // Access to the target data analysis pass const DataLayout * TD; @@ -65,19 +65,19 @@ namespace llvm { virtual bool runOnModule(Module & M); virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired(); + AU.addRequired(); } // Visitor methods for analyzing instructions //void visitInstruction(Instruction &I); - void visitCallSite(CallSite &CS); + void processCallSite(CallSite &CS); void visitCallInst(CallInst &CI) { CallSite CS(&CI); - visitCallSite(CS); + processCallSite(CS); } void visitInvokeInst(InvokeInst &II) { CallSite CS(&II); - visitCallSite(CS); + processCallSite(CS); } }; } diff --git a/lib/smack/CodifyStaticInits.cpp b/lib/smack/CodifyStaticInits.cpp index 406e3de44..60682c0c4 100644 --- a/lib/smack/CodifyStaticInits.cpp +++ b/lib/smack/CodifyStaticInits.cpp @@ -7,6 +7,7 @@ #include "smack/CodifyStaticInits.h" #include "smack/DSAWrapper.h" #include "smack/Debug.h" +#include "smack/InitializePasses.h" #include "smack/Naming.h" #include "smack/SmackOptions.h" #include "llvm/IR/DataLayout.h" @@ -28,8 +29,9 @@ bool CodifyStaticInits::runOnModule(Module &M) { LLVMContext &C = M.getContext(); DSAWrapper *DSA = &getAnalysis(); - Function *F = dyn_cast( - M.getOrInsertFunction(Naming::STATIC_INIT_PROC, Type::getVoidTy(C))); + Function *F = cast( + M.getOrInsertFunction(Naming::STATIC_INIT_PROC, Type::getVoidTy(C)) + .getCallee()); BasicBlock *B = BasicBlock::Create(C, "entry", F); IRBuilder<> IRB(B); @@ -95,10 +97,15 @@ void CodifyStaticInits::getAnalysisUsage(llvm::AnalysisUsage &AU) const { AU.addRequired(); } -// Pass ID variable -char CodifyStaticInits::ID = 0; +Pass *createCodifyStaticInitsPass() { return new CodifyStaticInits(); } -// Register the pass -static RegisterPass X("codify-static-inits", - "Codify Static Initializers"); } // namespace smack + +char smack::CodifyStaticInits::ID = 0; + +using namespace smack; +INITIALIZE_PASS_BEGIN(CodifyStaticInits, "codify-static-inits", + "Codify Static Initializers", false, false) +INITIALIZE_PASS_DEPENDENCY(DSAWrapper) +INITIALIZE_PASS_END(CodifyStaticInits, "codify-static-inits", + "Codify Static Initializers", false, false) diff --git a/lib/smack/DSAWrapper.cpp b/lib/smack/DSAWrapper.cpp index 32be24a5d..2968fc1e1 100644 --- a/lib/smack/DSAWrapper.cpp +++ b/lib/smack/DSAWrapper.cpp @@ -5,36 +5,32 @@ // the University of Illinois Open Source License. See LICENSE for details. // #include "smack/DSAWrapper.h" +#include "seadsa/InitializePasses.hh" #include "smack/Debug.h" +#include "smack/InitializePasses.h" #include "smack/SmackOptions.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/IntrinsicInst.h" - #include "llvm/Support/FileSystem.h" #include #include -#define DEBUG_TYPE "dsa-wrapper" +#define DEBUG_TYPE "smack-dsa-wrapper" namespace smack { using namespace llvm; -char DSAWrapper::ID; -RegisterPass - DSAWrapperPass("dsa-wrapper", - "SMACK Data Structure Graph Based Alias Analysis Wrapper"); - void DSAWrapper::getAnalysisUsage(llvm::AnalysisUsage &AU) const { AU.setPreservesAll(); - AU.addRequiredTransitive(); + AU.addRequiredTransitive(); } bool DSAWrapper::runOnModule(llvm::Module &M) { dataLayout = &M.getDataLayout(); - SD = &getAnalysis().getDsaAnalysis(); - assert(SD->kind() == sea_dsa::GlobalAnalysisKind::CONTEXT_INSENSITIVE && + SD = &getAnalysis().getDsaAnalysis(); + assert(SD->kind() == seadsa::GlobalAnalysisKind::CONTEXT_INSENSITIVE && "Currently we only want the context-insensitive sea-dsa."); DG = &SD->getGraph(*M.begin()); // Print the graph in dot format when debugging @@ -81,11 +77,11 @@ void DSAWrapper::countGlobalRefs() { } } -bool DSAWrapper::isStaticInitd(const sea_dsa::Node *n) { +bool DSAWrapper::isStaticInitd(const seadsa::Node *n) { return staticInits.count(n) > 0; } -bool DSAWrapper::isMemOpd(const sea_dsa::Node *n) { +bool DSAWrapper::isMemOpd(const seadsa::Node *n) { return memOpds.count(n) > 0; } @@ -112,7 +108,7 @@ unsigned DSAWrapper::getOffset(const Value *v) { return DG->getCell(*v).getOffset(); } -const sea_dsa::Node *DSAWrapper::getNode(const Value *v) { +const seadsa::Node *DSAWrapper::getNode(const Value *v) { // For sea-dsa, a node is obtained by getting the cell first. // It's possible that a value doesn't have a cell, e.g., undef. if (!DG->hasCell(*v)) @@ -124,13 +120,14 @@ const sea_dsa::Node *DSAWrapper::getNode(const Value *v) { bool DSAWrapper::isTypeSafe(const Value *v) { typedef std::unordered_map FieldMap; - typedef std::unordered_map NodeMap; + typedef std::unordered_map NodeMap; static NodeMap nodeMap; auto node = getNode(v); if (node->isOffsetCollapsed() || node->isExternal() || node->isIncomplete() || - node->isUnknown() || node->isIntToPtr() || node->isPtrToInt()) + node->isUnknown() || node->isIntToPtr() || node->isPtrToInt() || + isMemOpd(node)) // We consider it type-unsafe to be safe for these cases return false; @@ -199,7 +196,7 @@ bool DSAWrapper::isTypeSafe(const Value *v) { return false; } -unsigned DSAWrapper::getNumGlobals(const sea_dsa::Node *n) { +unsigned DSAWrapper::getNumGlobals(const seadsa::Node *n) { if (globalRefCount.count(n)) return globalRefCount[n]; else @@ -207,3 +204,15 @@ unsigned DSAWrapper::getNumGlobals(const sea_dsa::Node *n) { } } // namespace smack + +char smack::DSAWrapper::ID = 0; + +using namespace smack; +using namespace seadsa; +INITIALIZE_PASS_BEGIN(DSAWrapper, "smack-dsa-wrapper", + "SMACK Data Structure Graph Based Alias Analysis Wrapper", + false, false) +INITIALIZE_PASS_DEPENDENCY(DsaAnalysis) +INITIALIZE_PASS_END(DSAWrapper, "smack-dsa-wrapper", + "SMACK Data Structure Graph Based Alias Analysis Wrapper", + false, false) diff --git a/lib/smack/MemorySafetyChecker.cpp b/lib/smack/MemorySafetyChecker.cpp index ca88afa90..fb7167cc6 100644 --- a/lib/smack/MemorySafetyChecker.cpp +++ b/lib/smack/MemorySafetyChecker.cpp @@ -17,27 +17,17 @@ namespace smack { using namespace llvm; Function *MemorySafetyChecker::getLeakCheckFunction(Module &M) { - if (!leakCheckFunction.count(&M)) { - auto F = M.getFunction(Naming::MEMORY_LEAK_FUNCTION); - assert(F && "Memory leak check function must be present."); - leakCheckFunction[&M] = F; - } - return leakCheckFunction[&M]; + auto F = M.getFunction(Naming::MEMORY_LEAK_FUNCTION); + assert(F && "Memory leak check function must be present."); + return F; } Function *MemorySafetyChecker::getSafetyCheckFunction(Module &M) { - if (!safetyCheckFunction.count(&M)) { - auto &C = M.getContext(); - auto T = PointerType::getUnqual(Type::getInt8Ty(C)); - auto F = dyn_cast(M.getOrInsertFunction( - Naming::MEMORY_SAFETY_FUNCTION, - FunctionType::get(Type::getVoidTy(C), {T, T}, false))); - assert(F && "Memory safety function must be present."); - F->addFnAttr(Attribute::AttrKind::ReadNone); - F->addFnAttr(Attribute::AttrKind::NoUnwind); - safetyCheckFunction[&M] = F; - } - return safetyCheckFunction[&M]; + auto F = M.getFunction(Naming::MEMORY_SAFETY_FUNCTION); + assert(F && "Memory safety check function must be present."); + F->setDoesNotAccessMemory(); + F->setDoesNotThrow(); + return F; } void MemorySafetyChecker::insertMemoryLeakCheck(Instruction *I) { diff --git a/lib/smack/Naming.cpp b/lib/smack/Naming.cpp index 3317263e9..18d437e23 100644 --- a/lib/smack/Naming.cpp +++ b/lib/smack/Naming.cpp @@ -62,8 +62,11 @@ const std::string Naming::REC_MEM_OP = "boogie_si_record_mop"; const std::string Naming::MEM_OP_VAL = "$MOP"; const std::string Naming::RUST_ENTRY = "_ZN3std2rt10lang_start"; -const std::string Naming::RUST_PANIC1 = "_ZN4core9panicking5panic"; -const std::string Naming::RUST_PANIC2 = "_ZN3std9panicking11begin_panic"; +const std::vector Naming::RUST_PANICS = { + "_ZN3std9panicking15begin_panic_fmt17h", "_ZN4core9panicking5panic17h", + "_ZN3std9panicking11begin_panic17h", "_ZN4core9panicking9panic_fmt17h", + "_ZN4core9panicking18panic_bounds_check17h"}; + const std::string Naming::RUST_PANIC_ANNOTATION = "rust_panic"; const std::string Naming::BLOCK_LBL = "$bb"; @@ -82,6 +85,8 @@ const std::string Naming::CONTRACT_EXPR = "$expr"; const std::string Naming::MEMORY_SAFETY_FUNCTION = "__SMACK_check_memory_safety"; const std::string Naming::MEMORY_LEAK_FUNCTION = "__SMACK_check_memory_leak"; +const std::string Naming::INT_WRAP_SIGNED_FUNCTION = "$tos"; +const std::string Naming::INT_WRAP_UNSIGNED_FUNCTION = "$tou"; using namespace llvm; @@ -267,4 +272,8 @@ std::string Naming::freshVarName(const Value &V) { s << varNum++; return s.str(); } + +std::string Naming::getIntWrapFunc(bool isUnsigned) { + return isUnsigned ? INT_WRAP_UNSIGNED_FUNCTION : INT_WRAP_SIGNED_FUNCTION; +} } // namespace smack diff --git a/lib/smack/Prelude.cpp b/lib/smack/Prelude.cpp index c3bd83a9f..bcb867aed 100644 --- a/lib/smack/Prelude.cpp +++ b/lib/smack/Prelude.cpp @@ -2,6 +2,7 @@ #include "smack/Naming.h" #include "smack/Regions.h" #include "smack/SmackOptions.h" +#include "llvm/ADT/APInt.h" #include "llvm/Support/Casting.h" #include @@ -165,6 +166,12 @@ FuncDecl *builtinOp(std::string baseName, const Attr *attr, {attr}); } +std::string getIntLimit(unsigned size) { + auto n = APInt(size + 1, 0); + n.setBit(size); + return n.toString(10, false); +} + const std::vector IntOpGen::INTEGER_SIZES{ 1, 5, 6, 8, 16, 24, 32, 40, 48, 56, 64, 80, 88, 96, 128, 160, 256}; @@ -257,7 +264,8 @@ FuncDecl *extractValue(unsigned width) { void printFuncs(FuncsT funcs, std::stringstream &s) { for (auto &f : funcs) - s << f << "\n"; + if (f) + s << f << "\n"; } void describe(std::string comment, std::stringstream &s) { @@ -280,6 +288,9 @@ struct IntOpGen::IntArithOp : public IntOp { : IntOp(opName, arity, intOp, bvOp, alsoUsedByPtr) {} FuncDecl *getIntFunc(unsigned size) const { + if (!intOp) + return nullptr; + std::string type = getIntTypeName(size); std::string name = "$" + opName; @@ -302,6 +313,9 @@ struct IntOpGen::IntArithOp : public IntOp { } FuncDecl *getBvFunc(unsigned size) const { + if (!bvOp) + return nullptr; + std::string type = getBvTypeName(size); std::string name = "$" + opName; @@ -327,7 +341,8 @@ struct IntOpGen::IntArithOp : public IntOp { if (!SmackOptions::BitPrecise || (!SmackOptions::BitPrecisePointers && alsoUsedByPtr)) funcs.push_back(getIntFunc(size)); - if (SmackOptions::BitPrecise) + if (SmackOptions::BitPrecise || + (SmackOptions::BitPrecisePointers && alsoUsedByPtr)) funcs.push_back(getBvFunc(size)); return funcs; } @@ -352,10 +367,15 @@ struct IntOpGen::IntArithOp : public IntOp { // generate inlined int min/max function body such as `if (i1 < i2) then i1 // else i2` - template static const Expr *intMinMaxExpr(unsigned size) { + template + static const Expr *intMinMaxExpr(unsigned size) { const Expr *a1 = makeIntVarExpr(1); const Expr *a2 = makeIntVarExpr(2); - auto pred = MIN ? Expr::lt(a1, a2) : Expr::lt(a2, a1); + std::string predName = + std::string("$") + (SIGN ? "s" : "u") + (MIN ? "lt" : "gt"); + auto pred = Expr::fn( + indexedName(predName, {getIntTypeName(size), Naming::BOOL_TYPE}), a1, + a2); return Expr::ifThenElse(pred, a1, a2); } @@ -374,8 +394,10 @@ struct IntOpGen::IntArithOp : public IntOp { // for this case, the result of `srem` is the result of `mod` minus |i2| static const Expr *sremExpr(unsigned size) { std::string type = getIntTypeName(size); - const Expr *dividend = makeIntVarExpr(1); - const Expr *divisor = makeIntVarExpr(2); + const Expr *dividend = + IntOpGen::IntArithOp::wrappedExpr(size, makeIntVarExpr(1), false); + const Expr *divisor = + IntOpGen::IntArithOp::wrappedExpr(size, makeIntVarExpr(2), false); const Expr *zero = Expr::lit((unsigned long long)0); const Expr *mod = Expr::fn(indexedName("$smod", {type}), dividend, divisor); const Expr *modNeZero = @@ -393,8 +415,57 @@ struct IntOpGen::IntArithOp : public IntOp { // generate inlined `urem` function body like // $smod.i32(i1,i2), where `$smod` is a wrapper to SMT's `mod` function static const Expr *uremExpr(unsigned size) { - return Expr::fn(indexedName("$smod", {getIntTypeName(size)}), - makeIntVarExpr(1), makeIntVarExpr(2)); + const Expr *dividend = + IntOpGen::IntArithOp::wrappedExpr(size, makeIntVarExpr(1), true); + const Expr *divisor = + IntOpGen::IntArithOp::wrappedExpr(size, makeIntVarExpr(2), true); + return Expr::fn(indexedName("$smod", {getIntTypeName(size)}), dividend, + divisor); + } + + // generate inlined `tos` function body like + // `if i >= -128 && i < 128 then i else $smod(i + 128, 256) - 128` + static const Expr *tosExpr(unsigned size) { + auto i = makeIntVarExpr(0); + auto limitMinusOne = Expr::lit(getIntLimit(size - 1), 0); + auto c = Expr::and_( + new BinExpr(BinExpr::Gte, i, Expr::lit("-" + getIntLimit(size - 1), 0)), + new BinExpr(BinExpr::Lt, i, limitMinusOne)); + auto type = getIntTypeName(size); + return Expr::ifThenElse( + c, i, + Expr::fn( + indexedName("$sub", {type}), + Expr::fn(indexedName("$smod", {type}), + Expr::fn(indexedName("$add", {type}), i, limitMinusOne), + Expr::lit(getIntLimit(size), 0)), + limitMinusOne)); + } + + // generate inlined `tou` function body like + // `if i >= 0 && i < 256 then i else $smod.i8(i, 256)` + static const Expr *touExpr(unsigned size) { + auto i = makeIntVarExpr(0); + auto limit = Expr::lit(getIntLimit(size), 0); + auto c = Expr::and_(new BinExpr(BinExpr::Gte, i, Expr::lit(0ULL)), + new BinExpr(BinExpr::Lt, i, limit)); + auto type = getIntTypeName(size); + return Expr::ifThenElse(c, i, + Expr::fn(indexedName("$smod", {type}), i, limit)); + } + + static const Expr *wrappedExpr(unsigned size, const Expr *e, + bool isUnsigned) { + return SmackOptions::WrappedIntegerEncoding + ? Expr::fn(indexedName(Naming::getIntWrapFunc(isUnsigned), + {getIntTypeName(size)}), + e) + : e; + } + template static const Expr *divExpr(unsigned size) { + const Expr *a1 = wrappedExpr(size, makeIntVarExpr(1), !SIGN); + const Expr *a2 = wrappedExpr(size, makeIntVarExpr(2), !SIGN); + return Expr::fn(indexedName("$idiv", {getIntTypeName(size)}), a1, a2); } }; @@ -417,9 +488,14 @@ void IntOpGen::generateArithOps(std::stringstream &s) const { new InlinedOp( IntOpGen::IntArithOp::intArithExpr), bvBuiltinOp, true}, - {"sdiv", 2, intBuiltinOp, bvBuiltinOp, false}, + {"idiv", 2, intBuiltinOp, bvBuiltinOp, false}, + {"sdiv", 2, + new InlinedOp(IntOpGen::IntArithOp::divExpr), + bvBuiltinOp, false}, + {"udiv", 2, + new InlinedOp(IntOpGen::IntArithOp::divExpr), + bvBuiltinOp, false}, {"smod", 2, intBuiltinOp, bvBuiltinOp, false}, - {"udiv", 2, intBuiltinOp, bvBuiltinOp, false}, {"srem", 2, new InlinedOp(IntOpGen::IntArithOp::sremExpr), bvBuiltinOp, false}, {"urem", 2, new InlinedOp(IntOpGen::IntArithOp::uremExpr), @@ -432,23 +508,37 @@ void IntOpGen::generateArithOps(std::stringstream &s) const { {"xor", 2, uninterpretedOp, bvBuiltinOp, false}, {"nand", 2, uninterpretedOp, bvBuiltinOp, false}, {"not", 1, uninterpretedOp, bvBuiltinOp, false}, + {"tos", 1, + SmackOptions::WrappedIntegerEncoding + ? new InlinedOp(IntOpGen::IntArithOp::tosExpr) + : nullptr, + nullptr, true}, + {"tou", 1, + SmackOptions::WrappedIntegerEncoding + ? new InlinedOp(IntOpGen::IntArithOp::touExpr) + : nullptr, + nullptr, true}, {"smin", 2, - new InlinedOp(IntOpGen::IntArithOp::intMinMaxExpr), + new InlinedOp( + IntOpGen::IntArithOp::intMinMaxExpr), new InlinedOp( IntOpGen::IntArithOp::bvMinMaxExpr), false}, {"smax", 2, - new InlinedOp(IntOpGen::IntArithOp::intMinMaxExpr), + new InlinedOp( + IntOpGen::IntArithOp::intMinMaxExpr), new InlinedOp( IntOpGen::IntArithOp::bvMinMaxExpr), false}, {"umin", 2, - new InlinedOp(IntOpGen::IntArithOp::intMinMaxExpr), + new InlinedOp( + IntOpGen::IntArithOp::intMinMaxExpr), new InlinedOp( IntOpGen::IntArithOp::bvMinMaxExpr), false}, {"umax", 2, - new InlinedOp(IntOpGen::IntArithOp::intMinMaxExpr), + new InlinedOp( + IntOpGen::IntArithOp::intMinMaxExpr), new InlinedOp( IntOpGen::IntArithOp::bvMinMaxExpr), false}}; @@ -549,37 +639,82 @@ struct IntOpGen::IntPred : public IntOp { funcs.push_back(compFunc); funcs.push_back(predFunc); } - if (SmackOptions::BitPrecise) { + if (SmackOptions::BitPrecise || SmackOptions::BitPrecisePointers) { std::tie(compFunc, predFunc) = getBvFuncs(size); funcs.push_back(compFunc); funcs.push_back(predFunc); } return funcs; } + + template + static const Expr *intPredExpr(unsigned size) { + // SHAOBO: we apply modulo operations to the operands. + // Here is the reasoning: let's assume the cmp operation is unsigned, + // and there's a sequence of arithmetic operations which only contain + // addition, subtraction, multiplication. The inputs to such a computation + // f is from i_1 to i_n. The hypothesis we want to prove here is + // f(i_1,...,i_n) % B = f'(i_1 % B,...,i_n % B) where f' is the two's + // complement counterpart of f, and B is 2^m where m is the bitwidth of the + // operands. For certain operation o, its two's complement counterpart o' is + // equivalent to o(i_1,i_2) % B. The axioms we used for the proof is as + // follows, (X%B + Y%B)%B = (X+Y)%B, (X%B - Y%B)%B = (X-Y)%B, + // (X%B * Y%B)%B = (X*Y)%B. + // https://www.khanacademy.org/computing/computer-science/cryptography/modarithmetic/a/modular-addition-and-subtraction + // https://www.khanacademy.org/computing/computer-science/cryptography/modarithmetic/a/modular-multiplication + // so let's prove it inductively, for a computation f and its two + // subcomputation, f_1 and f_2 connected by o, by definition, we have, + // f(i_1,...,i_n) = o(f_1(i_1,...,i_n), f_2(i_1,...,i_n)) + // then, f(i_1,...,i_n)%B = o(f_1(i_1,...,i_n), f_2(i_1,...,i_n))%B + // following the axioms, we have, + // f(i_1,...,i_n)%B = o(f_1(i_1,...,i_n)%B, f_2(i_1,...,i_n)%B)%B + // by the definition of two's complement arithmetic, + // o(f_1(i_1,...,i_n)%B, f_2(i_1,...,i_n)%B)%B = + // o'(f_1(i_1,...,i_n)%B, f_2(i_1,...,i_n)%B) + // by induction, f_i(i_1,...,i_n)%B = f'_i(i_1%B,...,i_n%B) + // therefore, o'(f_1(i_1,...,i_n)%B, f_2(i_1,...,i_n)%B) = + // o'(f'_1(i_1%B,...,i_n%B), f_2'(i_1%B,...,i_n%B)) + // the rhs is exactly f' therefore we complete the proof. + // + // For signed comparison, the proof is trivial since we can get the precise + // two's complement representation following the proof above. + return new BinExpr( + OP, IntOpGen::IntArithOp::wrappedExpr(size, makeIntVarExpr(1), !SIGN), + IntOpGen::IntArithOp::wrappedExpr(size, makeIntVarExpr(2), !SIGN)); + } }; void IntOpGen::generatePreds(std::stringstream &s) const { describe("Integer predicates", s); const auto bvBuiltinOp = new BuiltinOp(IntOp::bvAttrFunc); - const auto leInlinedOp = new InlinedOp( - IntOpGen::IntArithOp::intArithExpr); - const auto ltInlinedOp = new InlinedOp( - IntOpGen::IntArithOp::intArithExpr); - const auto geInlinedOp = new InlinedOp( - IntOpGen::IntArithOp::intArithExpr); - const auto gtInlinedOp = new InlinedOp( - IntOpGen::IntArithOp::intArithExpr); + const auto uleInlinedOp = new InlinedOp( + IntOpGen::IntPred::intPredExpr); + const auto ultInlinedOp = new InlinedOp( + IntOpGen::IntPred::intPredExpr); + const auto ugeInlinedOp = new InlinedOp( + IntOpGen::IntPred::intPredExpr); + const auto ugtInlinedOp = new InlinedOp( + IntOpGen::IntPred::intPredExpr); + const auto sleInlinedOp = new InlinedOp( + IntOpGen::IntPred::intPredExpr); + const auto sltInlinedOp = new InlinedOp( + IntOpGen::IntPred::intPredExpr); + const auto sgeInlinedOp = new InlinedOp( + IntOpGen::IntPred::intPredExpr); + const auto sgtInlinedOp = new InlinedOp( + IntOpGen::IntPred::intPredExpr); + // normalize operands into 2's complement const auto eqInlinedOp = new InlinedOp( - IntOpGen::IntArithOp::intArithExpr); + IntOpGen::IntPred::intPredExpr); const auto neInlinedOp = new InlinedOp( - IntOpGen::IntArithOp::intArithExpr); + IntOpGen::IntPred::intPredExpr); const std::vector intPredTable{ - {"ule", leInlinedOp, bvBuiltinOp}, {"ult", ltInlinedOp, bvBuiltinOp}, - {"uge", geInlinedOp, bvBuiltinOp}, {"ugt", gtInlinedOp, bvBuiltinOp}, - {"sle", leInlinedOp, bvBuiltinOp}, {"slt", ltInlinedOp, bvBuiltinOp}, - {"sge", geInlinedOp, bvBuiltinOp}, {"sgt", gtInlinedOp, bvBuiltinOp}, - {"eq", eqInlinedOp, eqInlinedOp}, {"ne", neInlinedOp, neInlinedOp}}; + {"ule", uleInlinedOp, bvBuiltinOp}, {"ult", ultInlinedOp, bvBuiltinOp}, + {"uge", ugeInlinedOp, bvBuiltinOp}, {"ugt", ugtInlinedOp, bvBuiltinOp}, + {"sle", sleInlinedOp, bvBuiltinOp}, {"slt", sltInlinedOp, bvBuiltinOp}, + {"sge", sgeInlinedOp, bvBuiltinOp}, {"sgt", sgtInlinedOp, bvBuiltinOp}, + {"eq", eqInlinedOp, eqInlinedOp}, {"ne", neInlinedOp, neInlinedOp}}; for (auto &pred : intPredTable) for (auto size : INTEGER_SIZES) @@ -588,7 +723,7 @@ void IntOpGen::generatePreds(std::stringstream &s) const { struct IntOpGen::IntConv { typedef const Attr *(*attrT)(unsigned, unsigned); - typedef const Expr *(*idExprT)(); + typedef const Expr *(*castExprT)(unsigned, unsigned); typedef const Expr *(*truncExprT)(unsigned); std::string opName; bool upCast; @@ -596,14 +731,14 @@ struct IntOpGen::IntConv { Op *bvOp; FuncDecl *getIntFunc(unsigned size1, unsigned size2) const { - assert(isa>(intOp)); - auto iop = llvm::cast>(intOp); + assert(isa>(intOp)); + auto iop = llvm::cast>(intOp); std::string type1 = getIntTypeName(size1); std::string type2 = getIntTypeName(size2); // e.g.: function {:inline} $zext.i1.i5(i: i1) returns (i5) { i } return inlinedOp("$" + opName, {type1, type2}, makeIntVars(1, type1), type2, - ((idExprT)iop->func)()); + ((castExprT)iop->func)(size1, size2)); } FuncDecl *getBvFunc(unsigned size1, unsigned size2) const { @@ -632,10 +767,120 @@ struct IntOpGen::IntConv { } // generate identity expression such as `i1` - static const Expr *intIdentityExpr() { return makeIntVarExpr(0); } + static const Expr *intTruncExpr(unsigned size1, unsigned size2) { + // SHAOBO: from Ben, + // Let F be a computation with inputs i_1, ..., i_n and let T be a + // truncation operation from 2^A to 2^B where A > B. We want show that the + // truncation of a two's complement number with a bitwidth of A to a + // bitwidth of B is equivalent to modding that number by the base 2^B. In + // other words, we want to prove the hypothesis that + // + // T(F'(i_1 % 2^A, ..., i_n % 2^A)) = F(i_1, ..., i_n) % 2^B + // + // To do this, we use two equivalencies. First, notice that we proved + // earlier that + // + // F'(i_1 % A, ..., i_n % A) = F(i_1, ..., i_n) % A + // + // Also, by definition, + // + // trunc_A->B(x) = x % B + // + // This means that + // + // T(F'(i_1 % 2^A, ..., i_n % 2^A)) = T(F(i_1, ..., i_n) % 2^A) + // = (F(i_1, ..., i_n) % 2^A) % 2^B + // + // Next notice that F(i_1, ..., i_n) % 2^A = F(i_1, ..., i_n) - c * 2^A by + // definition for some integer c. Because of this, we can use the following + // axiom of modularity to simplify it: (X%M - Y%M)%M = (X-Y)%M + // https://www.khanacademy.org/computing/computer-science/cryptography/modarithmetic/a/modular-addition-and-subtraction + // + // (F(i_1, ..., i_n) - c * 2^A) % 2^B + // = ((F(i_1, ..., i_n) % 2^B) - (c * 2^A % 2^B)) % 2^B + // + // Since the second number is a multiple of 2^B, it goes to 0 and we're left + // with + // + // (F(i_1, ..., i_n) % 2^B - 0) % 2^B = F(i_1, ..., i_n) % 2^B + // + // Therefore, T(F'(i_1 % 2^A, ..., i_n % 2^A)) = F(i_1, ..., i_n) % 2^B + return IntOpGen::IntArithOp::wrappedExpr(size2, makeIntVarExpr(0), true); + } + + template + static const Expr *intExtExpr(unsigned size1, unsigned size2) { + // SHAOBO: from Ben + // Let F be a computation with inputs i_1, ..., i_n and let Z be an unsigned + // extension operation from 2^A to 2^B where A < B. We want show that the + // unsigned extension of a two's complement number with a bitwidth of A to a + // bitwidth of B is equivalent to modding that number by the base 2^A. In + // other words, we want to prove the hypothesis that + // + // Z(F'(i_1 % 2^A, ..., i_n % 2^A)) = F(i_1, ..., i_n) % 2^A + // + // To do this, we use two equivalencies. First, notice that we proved + // earlier that + // + // F'(i_1 % A, ..., i_n % A) = F(i_1, ..., i_n) % A + // + // Also, by definition, + // + // zext_A->B(x) = x % A + // + // This means that + // + // Z(F'(i_1 % 2^A, ..., i_n % 2^A)) = Z(F(i_1, ..., i_n) % 2^A) + // = (F(i_1, ..., i_n) % 2^A) % 2^A + // = F(i_1, ..., i_n) % 2^A + // + // Therefore, Z(F'(i_1 % 2^A, ..., i_n % 2^A)) = F(i_1, ..., i_n) % 2^A + // + // + // Let F be a computation with inputs i_1, ..., i_n and let S be a signed + // extension operation from 2^A to 2^B where A < B. We want show that the + // signed extension of a two's complement number with a bitwidth of A to a + // bitwidth of B is equivalent to converting that number to unsigned, + // modding the result by the base 2^A, and then converting back to signed. + // In other words, we want to prove the hypothesis that + // + // S(F'(i_1 % 2^A, ..., i_n % 2^A) - 2^(A-1)) + // = (F(i_1, ..., i_n) + 2^(A-1)) % 2^A - 2^(A-1) + // + // To do this, we use two equivalencies. First, notice that we proved + // earlier that + // + // F'(i_1 % A, ..., i_n % A) = F(i_1, ..., i_n) % A + // + // Also, by definition, + // + // sext_A->B(x) = (x + 2^(A-1)) % 2^A - 2^(A-1) + // + // This means that + // + // S(F'(i_1 % 2^A, ..., i_n % 2^A)) = S(F(i_1, ..., i_n) % 2^A) + // = (F(i_1, ..., i_n) % 2^A + 2^(A-1)) % 2^A - 2^(A-1) + // + // Because 2^(A-1) < 2^A, 2^(A-1) % 2^A = 2^(A-1). Lets rewrite this using + // that fact. + // + // (F(i_1, ..., i_n) % 2^A + 2^(A-1)) % 2^A - 2^(A-1) + // = (F(i_1, ..., i_n) % 2^A + 2^(A-1) % 2^A) % 2^A - 2^(A-1) + // + // Next, we can use the following axiom of modularity to simplify it: + // (X%M + Y%M)%M = (X+Y)%M + // https://www.khanacademy.org/computing/computer-science/cryptography/modarithmetic/a/modular-addition-and-subtraction + // + // (F(i_1, ..., i_n) % 2^A + 2^(A-1) % 2^A) % 2^A - 2^(A-1) + // = (F(i_1, ..., i_n) + 2^(A-1)) % 2^A - 2^(A-1) + // + // Therefore, S(F'(i_1 % 2^A, ..., i_n % 2^A) - 2^(A-1)) + // = (F(i_1, ..., i_n) + 2^(A-1)) % 2^A - 2^(A-1) + return IntOpGen::IntArithOp::wrappedExpr(size1, makeIntVarExpr(0), !SIGN); + } // generate bv truncation function body such as `i[1:0]` - static const Expr *truncExpr(unsigned size) { + static const Expr *bvTruncExpr(unsigned size) { return Expr::bvExtract(makeIntVarName(0), size, 0); } @@ -653,18 +898,20 @@ struct IntOpGen::IntConv { void IntOpGen::generateConvOps(std::stringstream &s) const { describe("Conversion between integer types", s); - const auto inlinedIdentity = - new InlinedOp(IntConv::intIdentityExpr); - const auto inlinedTrunc = - new InlinedOp(IntConv::truncExpr); + const auto intTrunc = + new InlinedOp(IntConv::intTruncExpr); + const auto intSext = + new InlinedOp(IntConv::intExtExpr); + const auto intZext = + new InlinedOp(IntConv::intExtExpr); + const auto bvTrunc = new InlinedOp(IntConv::bvTruncExpr); const auto builtinSext = new BuiltinOp(IntConv::extAttr); const auto builtinZext = new BuiltinOp(IntConv::extAttr); - const std::vector intConvTable{ - {"trunc", false, inlinedIdentity, inlinedTrunc}, - {"sext", true, inlinedIdentity, builtinSext}, - {"zext", true, inlinedIdentity, builtinZext}}; + const std::vector intConvTable{{"trunc", false, intTrunc, bvTrunc}, + {"sext", true, intSext, builtinSext}, + {"zext", true, intZext, builtinZext}}; for (auto &conv : intConvTable) { for (size_t s1 = 0; s1 < INTEGER_SIZES.size(); ++s1) { @@ -956,33 +1203,42 @@ void PtrOpGen::generatePtrNumConvs(std::stringstream &s) const { void PtrOpGen::generatePreds(std::stringstream &s) const { describe("Pointer predicates", s); - const std::vector predicates{"$eq", "$ne", "$ugt", "$uge", - "$ult", "$ule", "$sgt", "$sge", - "$slt", "$sle"}; + using PredInfo = std::pair; + const std::vector predicates{ + {"$eq", BinExpr::Eq}, {"$ne", BinExpr::Neq}, {"$ugt", BinExpr::Gt}, + {"$uge", BinExpr::Gte}, {"$ult", BinExpr::Lt}, {"$ule", BinExpr::Lte}, + {"$sgt", BinExpr::Gt}, {"$sge", BinExpr::Gte}, {"$slt", BinExpr::Lt}, + {"$sle", BinExpr::Lte}}; // e.g., function {:inline} $eq.ref(p1: ref, p2: ref) // returns (i1) { (if $eq.i64.bool(p1, p2) then 1 else 0) } - for (auto pred : predicates) { + for (auto info : predicates) { + auto predName = info.first; + auto binPred = info.second; + auto condExpr = Expr::fn( + indexedName(predName, {prelude.rep.pointerType(), Naming::BOOL_TYPE}), + {makePtrVarExpr(1), makePtrVarExpr(2)}); + const Expr *predExpr = + SmackOptions::BitPrecisePointers + ? condExpr + : new BinExpr(binPred, makePtrVarExpr(1), makePtrVarExpr(2)); + s << Decl::function( - indexedName(pred, {Naming::PTR_TYPE}), + indexedName(predName, {Naming::PTR_TYPE, Naming::BOOL_TYPE}), {{"p1", Naming::PTR_TYPE}, {"p2", Naming::PTR_TYPE}}, - prelude.rep.intType(1), - Expr::ifThenElse( - Expr::fn(indexedName(pred, {prelude.rep.pointerType(), - Naming::BOOL_TYPE}), - {Expr::id("p1"), Expr::id("p2")}), - prelude.rep.integerLit(1LL, 1), - prelude.rep.integerLit(0LL, 1)), - {makeInlineAttr()}) + Naming::BOOL_TYPE, predExpr, {makeInlineAttr()}) << "\n"; - s << Decl::function( - indexedName(pred, {Naming::PTR_TYPE, Naming::BOOL_TYPE}), - {{"p1", Naming::PTR_TYPE}, {"p2", Naming::PTR_TYPE}}, - Naming::BOOL_TYPE, - Expr::fn(indexedName( - pred, {prelude.rep.pointerType(), Naming::BOOL_TYPE}), - {Expr::id("p1"), Expr::id("p2")}), - {makeInlineAttr()}) + + s << Decl::function(indexedName(predName, {Naming::PTR_TYPE}), + {{"p1", Naming::PTR_TYPE}, {"p2", Naming::PTR_TYPE}}, + prelude.rep.intType(1), + Expr::ifThenElse( + Expr::fn(indexedName(predName, {Naming::PTR_TYPE, + Naming::BOOL_TYPE}), + {makePtrVarExpr(1), makePtrVarExpr(2)}), + prelude.rep.integerLit(1LL, 1), + prelude.rep.integerLit(0LL, 1)), + {makeInlineAttr()}) << "\n"; } s << "\n"; diff --git a/lib/smack/Regions.cpp b/lib/smack/Regions.cpp index 3d58c15eb..12602aefd 100644 --- a/lib/smack/Regions.cpp +++ b/lib/smack/Regions.cpp @@ -27,11 +27,11 @@ bool Region::isSingleton(const Value *v, unsigned length) { !node->isArray() && DSA->isTypeSafe(v) && !DSA->isMemOpd(node); } -bool Region::isAllocated(const sea_dsa::Node *N) { +bool Region::isAllocated(const seadsa::Node *N) { return N->isHeap() || N->isAlloca(); } -bool Region::isComplicated(const sea_dsa::Node *N) { +bool Region::isComplicated(const seadsa::Node *N) { return N->isIntToPtr() || N->isPtrToInt() || N->isExternal() || N->isUnknown(); } diff --git a/lib/smack/SimplifyLibCalls.cpp b/lib/smack/SimplifyLibCalls.cpp index f10350c59..605f161f6 100644 --- a/lib/smack/SimplifyLibCalls.cpp +++ b/lib/smack/SimplifyLibCalls.cpp @@ -8,7 +8,9 @@ #include "smack/Debug.h" #include "smack/Naming.h" #include "smack/SmackOptions.h" +#include "llvm/Analysis/BlockFrequencyInfo.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" +#include "llvm/Analysis/ProfileSummaryInfo.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" @@ -30,7 +32,9 @@ bool SimplifyLibCalls::runOnModule(Module &M) { modified = false; simplifier = new LibCallSimplifier( M.getDataLayout(), &getAnalysis().getTLI(), - getAnalysis().getORE()); + getAnalysis().getORE(), + &getAnalysis().getBFI(), + &getAnalysis().getPSI()); if (simplifier) visit(M); return modified; diff --git a/lib/smack/SmackInstGenerator.cpp b/lib/smack/SmackInstGenerator.cpp index cf9d14645..f8ca4c702 100644 --- a/lib/smack/SmackInstGenerator.cpp +++ b/lib/smack/SmackInstGenerator.cpp @@ -137,6 +137,17 @@ void SmackInstGenerator::annotate(llvm::Instruction &I, Block *B) { } } +bool isRustPanic(const std::string &name) { + for (const auto &panic : Naming::RUST_PANICS) { + // We are interested in exact functional matches. + // Rust mangled names include a 17 byte hash at the end. + if (name.find(panic) == 0 && name.size() == panic.size() + 17) { + return true; + } + } + return false; +} + void SmackInstGenerator::processInstruction(llvm::Instruction &inst) { SDEBUG(errs() << "Inst: " << inst << "\n"); annotate(inst, currBlock); @@ -615,15 +626,7 @@ void SmackInstGenerator::visitPHINode(llvm::PHINode &phi) { void SmackInstGenerator::visitSelectInst(llvm::SelectInst &i) { processInstruction(i); std::string x = naming->get(i); - const Expr *c = rep->expr(i.getCondition()), - *v1 = rep->expr(i.getTrueValue()), - *v2 = rep->expr(i.getFalseValue()); - - assert(!i.getCondition()->getType()->isVectorTy() && - "Vector condition is not supported."); - emit(Stmt::assign( - Expr::id(x), - Expr::ifThenElse(Expr::eq(c, rep->integerLit(1LL, 1)), v1, v2))); + emit(Stmt::assign(Expr::id(x), rep->select(&i))); } void SmackInstGenerator::visitCallInst(llvm::CallInst &ci) { @@ -648,8 +651,7 @@ void SmackInstGenerator::visitCallInst(llvm::CallInst &ci) { auto mainFunction = cast(castExpr); emit(Stmt::call(mainFunction->getName(), {}, {})); - } else if (name.find(Naming::RUST_PANIC1) != std::string::npos || - name.find(Naming::RUST_PANIC2) != std::string::npos) { + } else if (SmackOptions::RustPanics && isRustPanic(name)) { // Convert Rust's panic functions into assertion violations emit(Stmt::assert_(Expr::lit(false), {Attr::attr(Naming::RUST_PANIC_ANNOTATION)})); @@ -1159,6 +1161,7 @@ void SmackInstGenerator::visitIntrinsicInst(llvm::IntrinsicInst &ii) { {llvm::Intrinsic::ctpop, assignBvExpr(ctpop)}, {llvm::Intrinsic::cttz, cttz}, {llvm::Intrinsic::dbg_declare, ignore}, + {llvm::Intrinsic::dbg_label, ignore}, {llvm::Intrinsic::expect, identity}, {llvm::Intrinsic::fabs, assignUnFPFuncApp("$abs")}, {llvm::Intrinsic::fma, fma}, diff --git a/lib/smack/SmackOptions.cpp b/lib/smack/SmackOptions.cpp index 1376bede0..295e397af 100644 --- a/lib/smack/SmackOptions.cpp +++ b/lib/smack/SmackOptions.cpp @@ -38,11 +38,11 @@ const llvm::cl::opt SmackOptions::SourceLocSymbols( llvm::cl::desc("Include source locations in generated code.")); llvm::cl::opt SmackOptions::BitPrecise( - "bit-precise", llvm::cl::desc("Model non-pointer values as bit vectors.")); + "bit-precise", llvm::cl::desc("Model integer values as bit-vectors.")); const llvm::cl::opt SmackOptions::BitPrecisePointers( "bit-precise-pointers", - llvm::cl::desc("Model pointers and non-pointer values as bit vectors.")); + llvm::cl::desc("Model pointer values as bit-vectors.")); const llvm::cl::opt SmackOptions::AddTiming("timing-annotations", @@ -77,6 +77,15 @@ const llvm::cl::opt SmackOptions::LLVMAssumes( clEnumValN(LLVMAssumeType::check, "check", "enable checking of assume statements"))); +const llvm::cl::opt + SmackOptions::RustPanics("rust-panics", + llvm::cl::desc("Enable Rust panic checking")); + +const llvm::cl::opt SmackOptions::WrappedIntegerEncoding( + "wrapped-integer-encoding", + llvm::cl::desc( + "Enable wrapped integer arithmetic and signedness-aware comparison")); + bool SmackOptions::isEntryPoint(std::string name) { for (auto EP : EntryPoints) if (name == EP) diff --git a/lib/smack/SmackRep.cpp b/lib/smack/SmackRep.cpp index 825b1c958..40dfe0bef 100644 --- a/lib/smack/SmackRep.cpp +++ b/lib/smack/SmackRep.cpp @@ -818,6 +818,9 @@ const Expr *SmackRep::expr(const llvm::Value *v, bool isConstIntUnsigned) { else if (CE->isCompare()) return cmp(CE); + else if (CE->getOpcode() == Instruction::Select) + return select(CE); + else { SDEBUG(errs() << "VALUE : " << *constant << "\n"); llvm_unreachable("Constant expression of this type not supported."); @@ -922,12 +925,13 @@ const Expr *SmackRep::bop(unsigned opcode, const llvm::Value *lhs, } const Expr *SmackRep::cmp(const llvm::CmpInst *I) { - bool isUnsigned = I->isUnsigned(); - return cmp(I->getPredicate(), I->getOperand(0), I->getOperand(1), isUnsigned); + return cmp(I->getPredicate(), I->getOperand(0), I->getOperand(1), + I->isUnsigned()); } const Expr *SmackRep::cmp(const llvm::ConstantExpr *CE) { - return cmp(CE->getPredicate(), CE->getOperand(0), CE->getOperand(1), false); + return cmp(CE->getPredicate(), CE->getOperand(0), CE->getOperand(1), + llvm::CmpInst::isUnsigned((CmpInst::Predicate)CE->getPredicate())); } const Expr *SmackRep::cmp(unsigned predicate, const llvm::Value *lhs, @@ -943,6 +947,24 @@ const Expr *SmackRep::cmp(unsigned predicate, const llvm::Value *lhs, return Expr::fn(fn, e1, e2); } +const Expr *SmackRep::select(const llvm::SelectInst *I) { + return select(I->getCondition(), I->getTrueValue(), I->getFalseValue()); +} + +const Expr *SmackRep::select(const llvm::ConstantExpr *CE) { + return select(CE->getOperand(0), CE->getOperand(1), CE->getOperand(2)); +} + +const Expr *SmackRep::select(const llvm::Value *condVal, + const llvm::Value *trueVal, + const llvm::Value *falseVal) { + const Expr *c = expr(condVal), *v1 = expr(trueVal), *v2 = expr(falseVal); + + assert(!condVal->getType()->isVectorTy() && + "Vector condition is not supported."); + return Expr::ifThenElse(Expr::eq(c, integerLit(1LL, 1)), v1, v2); +} + bool SmackRep::isContractExpr(const llvm::Value *V) const { auto name = naming->get(*V); return isContractExpr(name); diff --git a/lib/utils/Devirt.cpp b/lib/utils/Devirt.cpp index a4b7c907f..5722830db 100644 --- a/lib/utils/Devirt.cpp +++ b/lib/utils/Devirt.cpp @@ -449,14 +449,14 @@ Devirtualize::makeDirectCall (CallSite & CS) { } // -// Method: visitCallSite() +// Method: processCallSite() // // Description: // Examine the specified call site. If it is an indirect call, mark it for // transformation into a direct call. // void -Devirtualize::visitCallSite (CallSite &CS) { +Devirtualize::processCallSite (CallSite &CS) { // // First, determine if this is a direct call. If so, then just ignore it. // @@ -491,7 +491,7 @@ Devirtualize::runOnModule (Module & M) { // // Get the targets of indirect function calls. // - CCG = &getAnalysis(); + CCG = &getAnalysis(); // // Get information on the target system. diff --git a/sea-dsa b/sea-dsa index 855c78b30..39ddfbfcb 160000 --- a/sea-dsa +++ b/sea-dsa @@ -1 +1 @@ -Subproject commit 855c78b304f275a5ed2f527d35ea4a53a7070338 +Subproject commit 39ddfbfcbc58287bca76bc7ff2539f7e97635eac diff --git a/share/smack/doctor.py b/share/smack/doctor.py index ef09832a0..1be8ff8f9 100755 --- a/share/smack/doctor.py +++ b/share/smack/doctor.py @@ -8,103 +8,127 @@ import sys import re import argparse -import platform + def red(text): - return '\033[0;31m' + text + '\033[0m' + return '\033[0;31m' + text + '\033[0m' + def green(text): - return '\033[0;32m' + text + '\033[0m' + return '\033[0;32m' + text + '\033[0m' + def check(text, condition): - global args - global count - if condition: - if not args.quiet: - print(green("[X] " + text)) - else: - print(red("[-] " + text), file=sys.stderr) - count += 1 + global args + global count + if condition: + if not args.quiet: + print(green("[X] " + text)) + else: + print(red("[-] " + text), file=sys.stderr) + count += 1 + def full_path(program): - for path in os.environ['PATH'].split(os.pathsep): - path = path.strip('"') - exe = os.path.join(path, program) - if os.path.isfile(exe) and os.access(exe, os.X_OK): - return exe - return None + for path in os.environ['PATH'].split(os.pathsep): + path = path.strip('"') + exe = os.path.join(path, program) + if os.path.isfile(exe) and os.access(exe, os.X_OK): + return exe + return None + def check_command(cmd): - exe = full_path(cmd) + exe = full_path(cmd) + + check("%s is in the path" % cmd, exe is not None) + if exe is not None: + try: + rc = Popen(cmd, stdout=PIPE, stderr=PIPE).wait() + except BaseException: + rc = None + check("%s is executable" % cmd, rc == 1 or rc == 2) - check("%s is in the path" % cmd, exe is not None) - if exe is not None: - try: - rc = Popen(cmd, stdout=PIPE, stderr=PIPE).wait() - except: - rc = None - check("%s is executable" % cmd, rc == 1 or rc == 2) def check_verifier(cmd): - exe = full_path(cmd) - var = cmd.upper() + exe = full_path(cmd) + var = cmd.upper() - if exe is not None: - check("%s is a bash script" % cmd, '#!/bin/bash' in open(exe).read()) - check("%s redirects to %s" % (cmd, var), ("$%s \"$@\"" % var) in open(exe).read()) + if exe is not None: + check("%s is a bash script" % cmd, '#!/bin/bash' in open(exe).read()) + check( + "%s redirects to %s" % + (cmd, var), ("$%s \"$@\"" % + var) in open(exe).read()) - check("%s environment variable is set" % var, var in os.environ) - if var in os.environ: - check("%s invokes mono" % var, re.match(r'\Amono', os.environ[var])) - verifier_exe = os.environ[var].split()[1] - check("%s verifier executable exists" % var, os.path.isfile(verifier_exe)) - solver_exe = os.path.join(os.path.dirname(verifier_exe), "z3.exe") - check("%s solver executable exists" % var, os.path.isfile(solver_exe)) - check("%s solver is executable" % var, os.access(solver_exe, os.X_OK)) + check("%s environment variable is set" % var, var in os.environ) + if var in os.environ: + check("%s invokes mono" % var, re.match(r'\Amono', os.environ[var])) + verifier_exe = os.environ[var].split()[1] + check("%s verifier executable exists" % + var, os.path.isfile(verifier_exe)) + solver_exe = os.path.join(os.path.dirname(verifier_exe), "z3.exe") + check("%s solver executable exists" % var, os.path.isfile(solver_exe)) + check("%s solver is executable" % var, os.access(solver_exe, os.X_OK)) + + check_command(cmd) - check_command(cmd) def check_headers(prefix): - HEADERS = [ - (["share", "smack", "include", "smack.h"], "#define SMACK_H_"), - (["share", "smack", "lib", "smack.c"], "void __SMACK_decls()") - ] + HEADERS = [ + (["share", "smack", "include", "smack.h"], "#define SMACK_H_"), + (["share", "smack", "lib", "smack.c"], "void __SMACK_decls()") + ] + + for (path, content) in HEADERS: + file = os.path.join(prefix, *path) + check("%s exists" % file, os.path.isfile(file)) + if os.path.isfile(file): + check( + "%s contains %s" % + (file, content), content in open(file).read()) - for (path, content) in HEADERS: - file = os.path.join(prefix, *path) - check("%s exists" % file, os.path.isfile(file)) - if os.path.isfile(file): - check("%s contains %s" % (file, content), content in open(file).read()) def main(): - global args - global count - parser = argparse.ArgumentParser(description='Diagnose SMACK configuration issues.') - parser.add_argument('-q', '--quiet', dest='quiet', action="store_true", default=False, - help='only show failed diagnostics') - parser.add_argument('--prefix', metavar='P', dest='prefix', type=str, default='', - help='point to the installation prefix') - args = parser.parse_args() - count = 0 - - if not args.quiet: - print("Checking front-end dependencies...") - check_command("clang") - check_command("clang++") - check_command("llvm-config") - check_command("llvm-link") - - if not args.quiet: - print("Checking back-end dependencies...") - check_verifier("boogie") - check_verifier("corral") - - if not args.quiet: - print("Checking SMACK itself...") - check_command("llvm2bpl") - check_command("smack") - - if args.prefix is not '': - check_headers(args.prefix) - - exit(count) + global args + global count + parser = argparse.ArgumentParser( + description='Diagnose SMACK configuration issues.') + parser.add_argument( + '-q', + '--quiet', + dest='quiet', + action="store_true", + default=False, + help='only show failed diagnostics') + parser.add_argument( + '--prefix', + metavar='P', + dest='prefix', + type=str, + default='', + help='point to the installation prefix') + args = parser.parse_args() + count = 0 + + if not args.quiet: + print("Checking front-end dependencies...") + check_command("clang") + check_command("clang++") + check_command("llvm-config") + check_command("llvm-link") + + if not args.quiet: + print("Checking back-end dependencies...") + check_verifier("boogie") + check_verifier("corral") + + if not args.quiet: + print("Checking SMACK itself...") + check_command("llvm2bpl") + check_command("smack") + + if not args.prefix: + check_headers(args.prefix) + + exit(count) diff --git a/share/smack/frontend.py b/share/smack/frontend.py index def79535e..35943bb02 100644 --- a/share/smack/frontend.py +++ b/share/smack/frontend.py @@ -1,309 +1,394 @@ import os import sys +import re +import json from .utils import temporary_file, try_command + def languages(): - """A dictionary of languages per file extension.""" - return { - 'c' : 'c', - 'i' : 'c', - 'cc' : 'cxx', - 'cpp' : 'cxx', - 'm' : 'objc', - 'd' : 'd', - 'json' : 'json', - 'svcomp' : 'svcomp', - 'bc' : 'llvm', - 'll' : 'llvm', - 'bpl' : 'boogie', - 'f' : 'fortran', - 'for' : 'fortran', - 'f90' : 'fortran', - 'f95' : 'fortran', - 'f03' : 'fortran', - 'rs' : 'rust', - } + """A dictionary of languages per file extension.""" + return { + 'c': 'c', + 'i': 'c', + 'cc': 'cxx', + 'cpp': 'cxx', + 'm': 'objc', + 'd': 'd', + 'json': 'json', + 'svcomp': 'svcomp', + 'bc': 'llvm', + 'll': 'llvm', + 'bpl': 'boogie', + 'f': 'fortran', + 'for': 'fortran', + 'f90': 'fortran', + 'f95': 'fortran', + 'f03': 'fortran', + 'rs': 'rust', + } + def frontends(): - """A dictionary of front-ends per language.""" - - # Avoid circular import - from .svcomp.utils import svcomp_frontend - - return { - 'c' : clang_frontend, - 'cxx' : clang_plusplus_frontend, - 'objc' : clang_objc_frontend, - 'd' : d_frontend, - 'json' : json_compilation_database_frontend, - 'svcomp' : svcomp_frontend, - 'llvm' : llvm_frontend, - 'boogie' : boogie_frontend, - 'fortran' : fortran_frontend, - 'rust' : rust_frontend, - } + """A dictionary of front-ends per language.""" + + # Avoid circular import + from .svcomp.utils import svcomp_frontend + + return { + 'c': clang_frontend, + 'cxx': clang_plusplus_frontend, + 'objc': clang_objc_frontend, + 'd': d_frontend, + 'json': json_compilation_database_frontend, + 'svcomp': svcomp_frontend, + 'llvm': llvm_frontend, + 'boogie': boogie_frontend, + 'fortran': fortran_frontend, + 'rust': rust_frontend, + } + def extra_libs(): - """A dictionary of extra SMACK libraries required by languages.""" - return { - 'fortran' : fortran_build_libs, - 'cxx' : cplusplus_build_libs, - # coming soon - libraries for OBJC, Rust, Swift, etc. - } + """A dictionary of extra SMACK libraries required by languages.""" + return { + 'fortran': fortran_build_libs, + 'cxx': cplusplus_build_libs, + 'rust': rust_build_libs, + # coming soon - libraries for OBJC, Rust, Swift, etc. + } def smack_root(): - return os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0]))) + return os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0]))) + def smack_header_path(): - return os.path.join(smack_root(), 'share', 'smack', 'include') + return os.path.join(smack_root(), 'share', 'smack', 'include') + def smack_headers(args): - paths = [] - paths.append(smack_header_path()) - return paths + paths = [] + paths.append(smack_header_path()) + return paths + def smack_lib(): - return os.path.join(smack_root(), 'share', 'smack', 'lib') - -def default_clang_compile_command(args, lib = False): - cmd = ['clang', '-c', '-emit-llvm', '-O0', '-g', '-gcolumn-info'] - # Starting from LLVM 5.0, we need the following two options - # in order to enable optimization passes. - # See: https://stackoverflow.com/a/46753969. - cmd += ['-Xclang', '-disable-O0-optnone'] - cmd += ['-I' + path for path in smack_headers(args)] - cmd += args.clang_options.split() - cmd += ['-DMEMORY_MODEL_' + args.mem_mod.upper().replace('-','_')] - if args.memory_safety: cmd += ['-DMEMORY_SAFETY'] - if args.integer_overflow: cmd += (['-fsanitize=signed-integer-overflow,shift'] if not lib else ['-DSIGNED_INTEGER_OVERFLOW_CHECK']) - if args.float: cmd += ['-DFLOAT_ENABLED'] - if args.bit_precise: cmd += ['-DBIT_PRECISE'] - if sys.stdout.isatty(): cmd += ['-fcolor-diagnostics'] - return cmd + return os.path.join(smack_root(), 'share', 'smack', 'lib') + + +def default_clang_compile_command(args, lib=False): + cmd = ['clang', '-c', '-emit-llvm', '-O0', '-g', '-gcolumn-info'] + # Starting from LLVM 5.0, we need the following two options + # in order to enable optimization passes. + # See: https://stackoverflow.com/a/46753969. + cmd += ['-Xclang', '-disable-O0-optnone'] + cmd += ['-I' + path for path in smack_headers(args)] + cmd += args.clang_options.split() + cmd += ['-DMEMORY_MODEL_' + args.mem_mod.upper().replace('-', '_')] + if ('memory-safety' in args.check or 'valid-deref' in args.check or + 'valid-free' in args.check or 'memleak' in args.check): + cmd += ['-DMEMORY_SAFETY'] + if 'integer-overflow' in args.check: + cmd += (['-fsanitize=signed-integer-overflow,shift'] + if not lib else ['-DSIGNED_INTEGER_OVERFLOW_CHECK']) + if args.float: + cmd += ['-DFLOAT_ENABLED'] + if args.pthread: + cmd += ['-DSMACK_MAX_THREADS=' + str(args.max_threads)] + if args.integer_encoding == 'bit-vector': + cmd += ['-DBIT_PRECISE'] + if sys.stdout.isatty(): + cmd += ['-fcolor-diagnostics'] + return cmd + def compile_to_bc(input_file, compile_command, args): - """Compile a source file to LLVM IR.""" - bc = temporary_file(os.path.splitext(os.path.basename(input_file))[0], '.bc', args) - try_command(compile_command + ['-o', bc, input_file], console=True) - return bc + """Compile a source file to LLVM IR.""" + bc = temporary_file( + os.path.splitext( + os.path.basename(input_file))[0], + '.bc', + args) + try_command(compile_command + ['-o', bc, input_file], console=True) + return bc + def d_compile_to_bc(input_file, compile_command, args): - """Compile a D source file to LLVM IR.""" - bc = temporary_file(os.path.splitext(os.path.basename(input_file))[0], '.bc', args) - try_command(compile_command + ['-of=' + bc, input_file], console=True) - return bc + """Compile a D source file to LLVM IR.""" + bc = temporary_file( + os.path.splitext( + os.path.basename(input_file))[0], + '.bc', + args) + try_command(compile_command + ['-of=' + bc, input_file], console=True) + return bc + def fortran_compile_to_bc(input_file, compile_command, args): - """Compile a FORTRAN source file to LLVM IR.""" - - # This method only exists as a hack to get flang to work - # with SMACK. When we update to the latest flang on LLVM 5, - # this method will no longer be necessary. The hack is - # self-contained in this method. - - # The Debug Info Version in flang is incompatible with - # the version that clang uses. The workaround is to use - # sed to change the file so llvm-link gives a warning - # and not an error. - - # compile to human-readable format in order to tweak the IR - compile_command[1] = '-S' - ll = temporary_file(os.path.splitext(os.path.basename(input_file))[0], '.ll', args) - try_command(compile_command + ['-o', ll, input_file], console=True) - # change the throw level of 'Debug Info Version' from error to warning in the IR - try_command(['sed', '-i', 's/i32 1, !\"Debug Info Version\"/i32 2, !\"Debug Info Version\"/g', ll]) - try_command(['llvm-as', ll]) - try_command(['rm', ll]) - bc = '.'.join(ll.split('.')[:-1] + ['bc']) - return bc + """Compile a FORTRAN source file to LLVM IR.""" + + # This method only exists as a hack to get flang to work + # with SMACK. When we update to the latest flang on LLVM 5, + # this method will no longer be necessary. The hack is + # self-contained in this method. + + # The Debug Info Version in flang is incompatible with + # the version that clang uses. The workaround is to use + # sed to change the file so llvm-link gives a warning + # and not an error. + + # compile to human-readable format in order to tweak the IR + compile_command[1] = '-S' + ll = temporary_file( + os.path.splitext( + os.path.basename(input_file))[0], + '.ll', + args) + try_command(compile_command + ['-o', ll, input_file], console=True) + # change the throw level of 'Debug Info Version' from error to warning in + # the IR + try_command( + ['sed', + '-i', + 's/i32 1, !\"Debug Info Version\"/i32 2, !\"Debug Info Version\"/g', + ll]) + try_command(['llvm-as', ll]) + try_command(['rm', ll]) + bc = '.'.join(ll.split('.')[:-1] + ['bc']) + return bc # Frontend functions here def llvm_frontend(input_file, args): - """Return LLVM IR file. Exists for symmetry with other frontends.""" + """Return LLVM IR file. Exists for symmetry with other frontends.""" + + return input_file - return input_file def clang_frontend(input_file, args): - """Generate LLVM IR from C-language source(s).""" + """Generate LLVM IR from C-language source(s).""" + + compile_command = default_clang_compile_command(args) + return compile_to_bc(input_file, compile_command, args) - compile_command = default_clang_compile_command(args) - return compile_to_bc(input_file,compile_command,args) def clang_plusplus_frontend(input_file, args): - """Generate LLVM IR from C++ language source(s).""" - compile_command = default_clang_compile_command(args) - compile_command[0] = 'clang++' - return compile_to_bc(input_file,compile_command,args) + """Generate LLVM IR from C++ language source(s).""" + compile_command = default_clang_compile_command(args) + compile_command[0] = 'clang++' + return compile_to_bc(input_file, compile_command, args) + def clang_objc_frontend(input_file, args): - """Generate LLVM IR from Objective-C language source(s).""" - - compile_command = default_clang_compile_command(args) - if sys.platform in ['linux', 'linux2']: - objc_flags = try_command(['gnustep-config', '--objc-flags']) - compile_command += objc_flags.split() - elif sys.platform == 'darwin': - sys.exit("Objective-C not yet supported on macOS") - else: - sys.exit("Objective-C not supported for this operating system.") - return compile_to_bc(input_file,compile_command,args) + """Generate LLVM IR from Objective-C language source(s).""" + + compile_command = default_clang_compile_command(args) + if sys.platform in ['linux', 'linux2']: + objc_flags = try_command(['gnustep-config', '--objc-flags']) + compile_command += objc_flags.split() + elif sys.platform == 'darwin': + sys.exit("Objective-C not yet supported on macOS") + else: + sys.exit("Objective-C not supported for this operating system.") + return compile_to_bc(input_file, compile_command, args) + def d_frontend(input_file, args): - """Generate Boogie code from D programming language source(s).""" + """Generate Boogie code from D programming language source(s).""" + + # note: -g and -O0 are not used here. + # Right now, it works, and with these options, smack crashes. + compile_command = ['ldc2', '-output-ll'] + compile_command += ['-I=' + path for path in smack_headers(args)] + args.entry_points += ['_Dmain'] + return d_compile_to_bc(input_file, compile_command, args) - # note: -g and -O0 are not used here. - # Right now, it works, and with these options, smack crashes. - compile_command = ['ldc2', '-output-ll'] - compile_command += ['-I=' + path for path in smack_headers(args)] - args.entry_points += ['_Dmain'] - return d_compile_to_bc(input_file,compile_command,args) def fortran_frontend(input_file, args): - """Generate Boogie code from Fortran language source(s).""" + """Generate Boogie code from Fortran language source(s).""" + + # For a fortran file that includes smack.f90 as a module, + # it will not compile unless the file 'smack.mod' exists + # in the working directory. 'smack.mod' is a build artifact + # of compiling smack.f90. Therefore, the solution is to + # compile smack.f90 before the source files. + fortran_build_libs(args) + # The result of this computation will be discarded when SMACK + # builds it's libraries later. - # For a fortran file that includes smack.f90 as a module, - # it will not compile unless the file 'smack.mod' exists - # in the working directory. 'smack.mod' is a build artifact - # of compiling smack.f90. Therefore, the solution is to - # compile smack.f90 before the source files. - fortran_build_libs(args) - # The result of this computation will be discarded when SMACK - # builds it's libraries later. + # replace the default entry point with the fortran default 'MAIN_' + args.entry_points += ['MAIN_'] - # replace the default entry point with the fortran default 'MAIN_' - args.entry_points += ['MAIN_'] + compile_command = default_clang_compile_command(args) + compile_command[0] = 'flang' - compile_command = default_clang_compile_command(args) - compile_command[0] = 'flang' + return fortran_compile_to_bc(input_file, compile_command, args) - return fortran_compile_to_bc(input_file,compile_command,args) def boogie_frontend(input_file, args): - """Pass Boogie code to the verifier.""" - if len(args.input_files) > 1: - raise RuntimeError("Expected a single Boogie file.") + """Pass Boogie code to the verifier.""" + if len(args.input_files) > 1: + raise RuntimeError("Expected a single Boogie file.") - with open(args.bpl_file, 'a+') as out: - with open(input_file) as f: - out.write(f.read()) + with open(args.bpl_file, 'a+') as out: + with open(input_file) as f: + out.write(f.read()) -def json_compilation_database_frontend(input_file, args): - """Generate Boogie code from a JSON compilation database.""" - if len(args.input_files) > 1: - raise RuntimeError("Expected a single JSON compilation database.") +def json_compilation_database_frontend(input_file, args): + """Generate Boogie code from a JSON compilation database.""" - output_flags = re.compile(r"-o ([^ ]*)[.]o\b") - optimization_flags = re.compile(r"-O[1-9]\b") + if len(args.input_files) > 1: + raise RuntimeError("Expected a single JSON compilation database.") - with open(input_file) as f: - for cc in json.load(f): - if 'objects' in cc: - # TODO what to do when there are multiple linkings? - bit_codes = [re.sub('[.]o$','.bc',f) for f in cc['objects']] - try_command(['llvm-link', '-o', args.bc_file] + bit_codes) - try_command(['llvm-link', '-o', args.linked_bc_file, args.bc_file] + build_libs(args)) + output_flags = re.compile(r"-o ([^ ]*)[.]o\b") + optimization_flags = re.compile(r"-O[1-9]\b") - else: - out_file = output_flags.findall(cc['command'])[0] + '.bc' - command = cc['command'] - command = output_flags.sub(r"-o \1.bc", command) - command = optimization_flags.sub("-O0", command) - command = command + " -emit-llvm" - try_command(command.split(),cc['directory'], console=True) + with open(input_file) as f: + for cc in json.load(f): + if 'objects' in cc: + # TODO what to do when there are multiple linkings? + bit_codes = [re.sub('[.]o$', '.bc', f) for f in cc['objects']] + try_command(['llvm-link', '-o', args.bc_file] + bit_codes) + try_command(['llvm-link', '-o', args.linked_bc_file, + args.bc_file] + default_build_libs(args)) + + else: + command = cc['command'] + command = output_flags.sub(r"-o \1.bc", command) + command = optimization_flags.sub("-O0", command) + command = command + " -emit-llvm" + try_command(command.split(), cc['directory'], console=True) + # import here to avoid a circular import + from .top import llvm_to_bpl + llvm_to_bpl(args) + + +def default_rust_compile_command(args): + compile_command = [ + 'rustc', + '-A', + 'unused-imports', + '-C', + 'opt-level=0', + '-C', + 'no-prepopulate-passes', + '-g', + '--cfg', + 'verifier="smack"', + '-C', + 'passes=name-anon-globals'] + return compile_command + args + + +def rust_build_rlib(input_file, args): + compile_command = default_rust_compile_command( + ['--crate-type', 'rlib,lib']) + rlib = temporary_file( + 'lib' + + os.path.splitext( + os.path.basename(input_file))[0], + '.rlib', + args) + try_command(compile_command + ['-o', rlib, input_file], console=True) + return rlib - llvm_to_bpl(args) def rust_frontend(input_file, args): - """Generate Boogie code from Rust programming language source(s).""" - compile_command = ['rustc', '-A', 'unused-imports', '-C', 'opt-level=0', - '-C', 'no-prepopulate-passes', '-g', '--emit=llvm-bc', - '--cfg', 'verifier="smack"', '-C', 'passes=name-anon-globals'] - - # This links in the Rust SMACK library. This is needed due to the way rustc - # finds a program's libraries. - abs_path = os.path.dirname(os.path.abspath(input_file)) - link_target = os.path.join(abs_path, "smack.rs") - link_source = os.path.join(smack_lib(), 'smack.rs') - try: - os.symlink(link_source, link_target) - except: - if not os.path.exists(link_source): - raise RuntimeError("Could not find or create smack module.") - # Otherwise okay - - return compile_to_bc(input_file,compile_command,args) + """Generate Boogie code from Rust programming language source(s).""" + rlib = rust_build_rlib(smack_lib() + '/smack.rs', args) + compile_command = default_rust_compile_command( + ['--emit=llvm-bc', '--extern', 'smack=' + rlib]) + + return compile_to_bc(input_file, compile_command, args) # Build libs functions here + def default_build_libs(args): - """Generate LLVM bitcodes for SMACK libraries.""" - bitcodes = [] - libs = ['smack.c', 'stdlib.c', 'errno.c'] + """Generate LLVM bitcodes for SMACK libraries.""" + bitcodes = [] + libs = ['smack.c', 'stdlib.c', 'errno.c'] - if args.pthread: - libs += ['pthread.c'] + if args.pthread: + libs += ['pthread.c'] - if args.strings or args.memory_safety or args.integer_overflow: - libs += ['string.c'] + if args.strings: + libs += ['string.c'] - if args.float: - libs += ['math.c'] - libs += ['fenv.c'] + if args.float: + libs += ['math.c'] + libs += ['fenv.c'] - compile_command = default_clang_compile_command(args, True) - for c in [os.path.join(smack_lib(), c) for c in libs]: - bc = compile_to_bc(c,compile_command,args) - bitcodes.append(bc) + compile_command = default_clang_compile_command(args, True) + for c in [os.path.join(smack_lib(), c) for c in libs]: + bc = compile_to_bc(c, compile_command, args) + bitcodes.append(bc) + + return bitcodes - return bitcodes def fortran_build_libs(args): - """Generate FORTRAN-specific LLVM bitcodes for SMACK libraries.""" + """Generate FORTRAN-specific LLVM bitcodes for SMACK libraries.""" + + bitcodes = [] + libs = ['smack.f90'] - bitcodes = [] - libs = ['smack.f90'] + compile_command = default_clang_compile_command(args) + compile_command[0] = 'flang' - compile_command = default_clang_compile_command(args) - compile_command[0] = 'flang' + for c in [os.path.join(smack_lib(), c) for c in libs]: + bc = fortran_compile_to_bc(c, compile_command, args) + bitcodes.append(bc) - for c in [os.path.join(smack_lib(), c) for c in libs]: - bc = fortran_compile_to_bc(c,compile_command,args) - bitcodes.append(bc) + return bitcodes - return bitcodes def cplusplus_build_libs(args): - """Generate C++ specific LLVM bitcodes for SMACK libraries.""" + """Generate C++ specific LLVM bitcodes for SMACK libraries.""" + + bitcodes = [] + libs = ['smack.cpp'] + + compile_command = default_clang_compile_command(args, True) + compile_command[0] = 'clang++' - bitcodes = [] - libs = ['smack.cpp'] + for c in [os.path.join(smack_lib(), c) for c in libs]: + bc = compile_to_bc(c, compile_command, args) + bitcodes.append(bc) - compile_command = default_clang_compile_command(args,True) - compile_command[0] = 'clang++' + return bitcodes - for c in [os.path.join(smack_lib(), c) for c in libs]: - bc = compile_to_bc(c,compile_command,args) - bitcodes.append(bc) - return bitcodes +def rust_build_libs(args): + """Generate Rust specific LLVM bitcodes for SMACK libraries.""" + bitcodes = [] + libs = ['smack.rs'] + + compile_command = default_rust_compile_command( + ['--emit=llvm-bc', '--crate-type', 'lib']) + + for c in [os.path.join(smack_lib(), c) for c in libs]: + bc = compile_to_bc(c, compile_command, args) + bitcodes.append(bc) + + return bitcodes # llvm link files + def link_bc_files(bitcodes, libs, args): - """Link generated LLVM bitcode and relevant smack libraries.""" + """Link generated LLVM bitcode and relevant smack libraries.""" - smack_libs = default_build_libs(args) - for build_lib in libs: - smack_libs += build_lib(args) + smack_libs = default_build_libs(args) + for build_lib in libs: + smack_libs += build_lib(args) - try_command(['llvm-link', '-o', args.bc_file] + bitcodes) - try_command(['llvm-link', '-o', args.linked_bc_file, args.bc_file] + smack_libs) - - # import here to avoid a circular import - from .top import llvm_to_bpl - llvm_to_bpl(args) + try_command(['llvm-link', '-o', args.bc_file] + bitcodes) + try_command(['llvm-link', '-o', args.linked_bc_file, + args.bc_file] + smack_libs) + # import here to avoid a circular import + from .top import llvm_to_bpl + llvm_to_bpl(args) diff --git a/share/smack/include/smack.h b/share/smack/include/smack.h index 30fa3d297..19f755c50 100644 --- a/share/smack/include/smack.h +++ b/share/smack/include/smack.h @@ -55,7 +55,6 @@ void __VERIFIER_assume(int); #ifndef CUSTOM_VERIFIER_ASSERT void __VERIFIER_assert(int); #endif -void __VERIFIER_error(void); #ifndef AVOID_NAME_CONFLICTS #define assert(EX) \ diff --git a/share/smack/lib/pthread.c b/share/smack/lib/pthread.c index f15608683..6cf987cfd 100644 --- a/share/smack/lib/pthread.c +++ b/share/smack/lib/pthread.c @@ -4,6 +4,8 @@ #include "pthread.h" #include "smack.h" +void *__SMACK_PthreadReturn[SMACK_MAX_THREADS]; + void __SMACK_init_tidtype() { #ifdef BIT_PRECISE __SMACK_top_decl("type $tidtype = bv32;"); @@ -20,21 +22,21 @@ void __SMACK_init_func_corral_primitives() { void __SMACK_init_func_thread() { // Array and possible statuses for tracking pthreads - __SMACK_top_decl("//dim0=tid, dim1= idx 0 gets status, 1 gets return value"); - __SMACK_top_decl("var $pthreadStatus: [$tidtype][int]int;"); + __SMACK_top_decl("var $pthreadStatus: [$tidtype]int;"); __SMACK_top_decl("const unique $pthread_uninitialized: int;"); __SMACK_top_decl("const unique $pthread_initialized: int;"); __SMACK_top_decl("const unique $pthread_waiting: int;"); __SMACK_top_decl("const unique $pthread_running: int;"); __SMACK_top_decl("const unique $pthread_stopped: int;"); // Initialize this array so all threads begin as uninitialized - __SMACK_code("assume (forall i:$tidtype :: $pthreadStatus[i][0] == " + __SMACK_code("assume (forall i:$tidtype :: $pthreadStatus[i] == " "$pthread_uninitialized);"); } pthread_t pthread_self(void) { int tmp_tid = __VERIFIER_nondet_int(); __SMACK_code("call @ := corral_getThreadID();", tmp_tid); + __VERIFIER_assume(tmp_tid < SMACK_MAX_THREADS); // Print actual tid to SMACK traces int actual_tid = tmp_tid; @@ -58,14 +60,10 @@ int pthread_join(pthread_t __th, void **__thread_return) { return 35; // This is EDEADLK // Wait for the thread to terminate - __SMACK_code("assume $pthreadStatus[@][0] == $pthread_stopped;", __th); - - // Get the thread's return value - void *tmp_thread_return_pointer = (void *)__VERIFIER_nondet_long(); - __SMACK_code("@ := $pthreadStatus[@][1];", tmp_thread_return_pointer, __th); + __SMACK_code("assume $pthreadStatus[@] == $pthread_stopped;", __th); if (__thread_return) { - *__thread_return = tmp_thread_return_pointer; + *__thread_return = __SMACK_PthreadReturn[__th]; // Print return pointer value to SMACK traces void *actual_thread_return_pointer = *__thread_return; @@ -85,9 +83,9 @@ void pthread_exit(void *retval) { // Ensure exit hasn't already been called #ifndef DISABLE_PTHREAD_ASSERTS - __SMACK_code("assert $pthreadStatus[@][0] == $pthread_running;", tid); + __SMACK_code("assert $pthreadStatus[@] == $pthread_running;", tid); #endif - __SMACK_code("$pthreadStatus[@][1] := @;", tid, retval); + __SMACK_PthreadReturn[tid] = retval; // Set return pointer value for display in SMACK traces void *pthread_return_pointer = retval; @@ -261,18 +259,16 @@ void __call_wrapper(pthread_t *__newthread, void *(*__start_routine)(void *), // Cycle through thread statuses properly, as thread is started, run, // and stopped. - __SMACK_code("$pthreadStatus[@][0] := $pthread_waiting;", ctid); - __SMACK_code("$pthreadStatus[@][0] := $pthread_running;", ctid); + __SMACK_code("$pthreadStatus[@] := $pthread_waiting;", ctid); + __SMACK_code("$pthreadStatus[@] := $pthread_running;", ctid); __start_routine(__arg); - __SMACK_code("$pthreadStatus[@][0] := $pthread_stopped;", ctid); + __SMACK_code("$pthreadStatus[@] := $pthread_stopped;", ctid); } int pthread_create(pthread_t *__newthread, __const pthread_attr_t *__attr, void *(*__start_routine)(void *), void *__arg) { - pthread_t tmp = __VERIFIER_nondet_int(); - - // Add unreachable C-level call to __call_wrapper, so llvm sees + // Add unreachable C-level call to __call_wrapper, so LLVM sees // the call to __call_wrapper and performs DSA on it. int x = __VERIFIER_nondet_int(); __VERIFIER_assume(x == 0); @@ -281,7 +277,9 @@ int pthread_create(pthread_t *__newthread, __const pthread_attr_t *__attr, __SMACK_code("async call @(@, @, @);", __call_wrapper, __newthread, __start_routine, __arg); + pthread_t tmp = __VERIFIER_nondet_int(); __SMACK_code("call @ := corral_getChildThreadID();", tmp); + __VERIFIER_assume(tmp < SMACK_MAX_THREADS); *__newthread = tmp; return 0; diff --git a/share/smack/lib/smack.c b/share/smack/lib/smack.c index c984ad493..61c7651c0 100644 --- a/share/smack/lib/smack.c +++ b/share/smack/lib/smack.c @@ -47,17 +47,6 @@ void __VERIFIER_assert(int x) { } #endif -void __VERIFIER_error(void) { -#if !MEMORY_SAFETY && !SIGNED_INTEGER_OVERFLOW_CHECK - __SMACK_code("assert false;"); -#elif MEMORY_SAFETY - __SMACK_code("assert {:valid_memtrack} $allocatedCounter == 0;"); - __SMACK_code("assume false;"); -#else - __SMACK_code("assume false;"); -#endif -} - void __SMACK_check_overflow(int flag) { __SMACK_dummy(flag); __SMACK_code("assert {:overflow} @ == $0;", flag); diff --git a/share/smack/lib/smack.rs b/share/smack/lib/smack.rs index 0b3a86b19..21ec33bc3 100644 --- a/share/smack/lib/smack.rs +++ b/share/smack/lib/smack.rs @@ -1,434 +1,503 @@ +#![crate_type = "staticlib"] + +#[cfg(verifier = "smack")] +extern "C" { + pub fn __VERIFIER_assert(x: i32); + pub fn __VERIFIER_assume(x: i32); + pub fn __VERIFIER_nondet_signed_char() -> i8; + pub fn __VERIFIER_nondet_unsigned_char() -> u8; + pub fn __VERIFIER_nondet_signed_short() -> i16; + pub fn __VERIFIER_nondet_unsigned_short() -> u16; + pub fn __VERIFIER_nondet_signed_int() -> i32; + pub fn __VERIFIER_nondet_unsigned_int() -> u32; + pub fn __VERIFIER_nondet_signed_long_long() -> i64; + pub fn __VERIFIER_nondet_unsigned_long_long() -> u64; + pub fn malloc(size: usize) -> *mut u8; + pub fn __VERIFIER_memcpy(dest: *mut u8, src: *mut u8, count: usize) -> *mut u8; + pub fn free(ptr: *mut u8); +} + +#[macro_export] +macro_rules! verifier_assert { + ( $cond:expr ) => { + unsafe { + __VERIFIER_assert($cond as i32); + }; + }; +} + #[cfg(verifier = "smack")] -extern { - pub fn __VERIFIER_assert(x: i32); - pub fn __VERIFIER_assume(x: i32); - pub fn __VERIFIER_nondet_signed_char() -> i8; - pub fn __VERIFIER_nondet_unsigned_char() -> u8; - pub fn __VERIFIER_nondet_signed_short() -> i16; - pub fn __VERIFIER_nondet_unsigned_short() -> u16; - pub fn __VERIFIER_nondet_signed_int() -> i32; - pub fn __VERIFIER_nondet_unsigned_int() -> u32; - pub fn __VERIFIER_nondet_signed_long_long() -> i64; - pub fn __VERIFIER_nondet_unsigned_long_long() -> u64; - pub fn malloc(size: usize) -> *mut u8; - pub fn __VERIFIER_memcpy(dest: *mut u8, src: *mut u8, count:usize) -> *mut u8; - pub fn free(ptr: *mut u8); +#[macro_export] +macro_rules! print { + ($fmt:expr) => (); + ($fmt:expr, $($arg:expr),*) => ( $($arg);* ) } +#[cfg(verifier = "smack")] +#[macro_export] +macro_rules! println { + ($($arg:tt)*) => ( print!($($arg)*) ) +} + +#[cfg(verifier = "smack")] +#[macro_export] +macro_rules! eprint { + ($($arg:tt)*) => ( print!($($arg)*) ) +} + +#[cfg(verifier = "smack")] +#[macro_export] +macro_rules! eprintln { + ($($arg:tt)*) => ( print!($($arg)*) ) +} #[cfg(verifier = "smack")] #[macro_export] macro_rules! assert { - ( $cond:expr ) => - ( - unsafe { __VERIFIER_assert($cond as i32); }; - ) + ( $cond:expr ) => { + verifier_assert!($cond); + }; } #[cfg(verifier = "smack")] #[macro_export] macro_rules! assert_eq { - ( $lhs:expr, $rhs:expr ) => ( assert!($lhs == $rhs); ) + ( $lhs:expr, $rhs:expr ) => { + smack::assert!($lhs == $rhs); + }; } #[cfg(verifier = "smack")] #[macro_export] macro_rules! assert_neq { - ( $lhs:expr, $rhs:expr ) => ( assert!($lhs != $rhs); ) + ( $lhs:expr, $rhs:expr ) => { + smack::assert!($lhs != $rhs); + }; } #[macro_export] -macro_rules! assume { - ( $cond:expr ) => - ( - #[cfg(verifier = "smack")] - unsafe { __VERIFIER_assume($cond as i32); } +macro_rules! verifier_assume { + ( $cond:expr ) => { + #[cfg(verifier = "smack")] + unsafe { + __VERIFIER_assume($cond as i32); + } - #[cfg(not(verifier = "smack"))] - (); - ) + #[cfg(not(verifier = "smack"))] + (); + }; +} + +#[macro_export] +macro_rules! assume { + ( $cond:expr ) => { + verifier_assume!($cond); + }; } #[macro_export] -macro_rules! nondet { +macro_rules! verifier_nondet { ($e:expr) => ( #[cfg(verifier = "smack")] - $e.nondet() + $e.verifier_nondet() #[cfg(not(verifier = "smack"))] $e ) } -pub trait NonDet { - fn nondet(self) -> Self; +pub trait VerifierNonDet { + fn verifier_nondet(self) -> Self; } #[macro_export] -macro_rules! make_nondet { - ($typ:ident, $nondet:ident) => - ( - impl NonDet for $typ { - #[cfg(verifier = "smack")] - fn nondet(self) -> Self { - unsafe { $nondet() as Self } - } - - #[cfg(not(verifier = "smack"))] - fn nondet(self) -> Self { - self +macro_rules! make_verifier_nondet { + ($typ:ident, $nondet:ident) => { + impl VerifierNonDet for $typ { + #[cfg(verifier = "smack")] + fn verifier_nondet(self) -> Self { + unsafe { $nondet() as Self } + } + + #[cfg(not(verifier = "smack"))] + fn verifier_nondet(self) -> Self { + self + } } - } - ); + }; } /* Instantiate nondet for all integer types. */ -make_nondet!(i8, __VERIFIER_nondet_signed_char); -make_nondet!(u8, __VERIFIER_nondet_unsigned_char); -make_nondet!(i16, __VERIFIER_nondet_signed_short); -make_nondet!(u16, __VERIFIER_nondet_unsigned_short); -make_nondet!(i32, __VERIFIER_nondet_signed_int); -make_nondet!(u32, __VERIFIER_nondet_unsigned_int); -make_nondet!(i64, __VERIFIER_nondet_signed_long_long); -make_nondet!(u64, __VERIFIER_nondet_unsigned_long_long); -make_nondet!(isize, __VERIFIER_nondet_signed_long_long); -make_nondet!(usize, __VERIFIER_nondet_unsigned_long_long); - +make_verifier_nondet!(i8, __VERIFIER_nondet_signed_char); +make_verifier_nondet!(u8, __VERIFIER_nondet_unsigned_char); +make_verifier_nondet!(i16, __VERIFIER_nondet_signed_short); +make_verifier_nondet!(u16, __VERIFIER_nondet_unsigned_short); +make_verifier_nondet!(i32, __VERIFIER_nondet_signed_int); +make_verifier_nondet!(u32, __VERIFIER_nondet_unsigned_int); +make_verifier_nondet!(i64, __VERIFIER_nondet_signed_long_long); +make_verifier_nondet!(u64, __VERIFIER_nondet_unsigned_long_long); +make_verifier_nondet!(isize, __VERIFIER_nondet_signed_long_long); +make_verifier_nondet!(usize, __VERIFIER_nondet_unsigned_long_long); #[cfg(not(verifier = "smack"))] #[cfg(feature = "std")] #[allow(dead_code)] use std::Vec; /* Vector class. - Based on https://doc.rust-lang.org/nomicon/vec-final.html */ +Based on https://doc.rust-lang.org/nomicon/vec-final.html */ #[cfg(verifier = "smack")] #[allow(dead_code)] fn sized_realloc(orig_ptr: *mut u8, orig_size: usize, new_size: usize) -> *mut u8 { - unsafe { - let result: *mut u8 = malloc(new_size); - __VERIFIER_memcpy(result, orig_ptr, orig_size); - result - } + unsafe { + let result: *mut u8 = malloc(new_size); + __VERIFIER_memcpy(result, orig_ptr, orig_size); + result + } } -#[cfg(verifier = "smack")] -use std::ptr::{self,null}; #[cfg(verifier = "smack")] use std::mem; #[cfg(verifier = "smack")] use std::ops::{Deref, DerefMut}; +#[cfg(verifier = "smack")] +use std::ptr::{self, null}; #[cfg(verifier = "smack")] #[allow(dead_code)] pub struct PhantomData { - _place_holder: *const T, - _padding: u64 + _place_holder: *const T, + _padding: u64, } #[cfg(verifier = "smack")] impl Default for PhantomData { fn default() -> Self { - PhantomData:: { _place_holder: ptr::null(), - _padding: 0} + PhantomData:: { + _place_holder: ptr::null(), + _padding: 0, + } } } #[cfg(verifier = "smack")] #[allow(dead_code)] struct Unique { -// _marker: PhantomData, // For the drop checker - ptr: *const T, // *const for variance + // _marker: PhantomData, // For the drop checker + ptr: *const T, // *const for variance _marker: u64, } #[cfg(verifier = "smack")] impl Unique { - pub fn new(ptr: *mut T) -> Self { - Unique { ptr: ptr, _marker: Default::default() } - } + pub fn new(ptr: *mut T) -> Self { + Unique { + ptr: ptr, + _marker: Default::default(), + } + } - pub fn as_ptr(&self) -> *mut T { - self.ptr as *mut T - } + pub fn as_ptr(&self) -> *mut T { + self.ptr as *mut T + } } #[cfg(verifier = "smack")] #[allow(dead_code)] struct RawVec { - ptr: Unique, - cap: usize, + ptr: Unique, + cap: usize, } #[cfg(verifier = "smack")] #[allow(dead_code)] impl RawVec { - fn new() -> Self { - let elem_size = mem::size_of::(); - let cap = 32; - let ptr = unsafe { Unique::new(malloc(cap*elem_size) as *mut T) }; - RawVec { ptr: ptr, cap: cap } - } - + fn new() -> Self { + let elem_size = mem::size_of::(); + let cap = 32; + let ptr = unsafe { Unique::new(malloc(cap * elem_size) as *mut T) }; + RawVec { ptr: ptr, cap: cap } + } - fn new_with_capacity(cap: usize) -> Self { - let elem_size = mem::size_of::(); - let ptr = unsafe { Unique::new(malloc(cap*elem_size) as *mut T) }; - RawVec { ptr: ptr, cap: cap } - } + fn new_with_capacity(cap: usize) -> Self { + let elem_size = mem::size_of::(); + let ptr = unsafe { Unique::new(malloc(cap * elem_size) as *mut T) }; + RawVec { ptr: ptr, cap: cap } + } fn grow(&mut self) { - let elem_size = mem::size_of::(); - let new_cap = 2 * self.cap; - let ptr = sized_realloc(self.ptr.as_ptr() as *mut _, self.cap*elem_size, new_cap*elem_size); - - self.ptr = Unique::new(ptr as *mut _); - self.cap = new_cap; - } + let elem_size = mem::size_of::(); + let new_cap = 2 * self.cap; + let ptr = sized_realloc( + self.ptr.as_ptr() as *mut _, + self.cap * elem_size, + new_cap * elem_size, + ); + + self.ptr = Unique::new(ptr as *mut _); + self.cap = new_cap; + } } #[cfg(verifier = "smack")] impl Drop for RawVec { - fn drop(&mut self) { - unsafe { free(self.ptr.ptr as *mut _) }; - } + fn drop(&mut self) { + unsafe { free(self.ptr.ptr as *mut _) }; + } } #[cfg(verifier = "smack")] pub struct Vec { - buf: RawVec, - len: usize, + buf: RawVec, + len: usize, } #[cfg(verifier = "smack")] impl Vec { - fn ptr(&self) -> *mut T { self.buf.ptr.as_ptr() } + fn ptr(&self) -> *mut T { + self.buf.ptr.as_ptr() + } - #[allow(dead_code)] - fn cap(&self) -> usize { self.buf.cap } + #[allow(dead_code)] + fn cap(&self) -> usize { + self.buf.cap + } - pub fn new() -> Self { - Vec { buf: RawVec::new(), len: 0 } - } + pub fn new() -> Self { + Vec { + buf: RawVec::new(), + len: 0, + } + } - #[allow(dead_code)] - pub fn with_capacity(cap: usize) -> Self { - Vec { buf: RawVec::new_with_capacity(cap), len: 0 } - } - + #[allow(dead_code)] + pub fn with_capacity(cap: usize) -> Self { + Vec { + buf: RawVec::new_with_capacity(cap), + len: 0, + } + } - #[allow(dead_code)] - pub fn push(&mut self, elem: T) { - if self.len == self.cap() { self.buf.grow(); } + #[allow(dead_code)] + pub fn push(&mut self, elem: T) { + if self.len == self.cap() { + self.buf.grow(); + } - unsafe { - ptr::write(self.ptr().offset(self.len as isize), elem); + unsafe { + ptr::write(self.ptr().offset(self.len as isize), elem); + } + + self.len += 1; } - self.len += 1; - } + #[allow(dead_code)] + pub fn pop(&mut self) -> Option { + if self.len == 0 { + None + } else { + self.len -= 1; + unsafe { Some(ptr::read(self.ptr().offset(self.len as isize))) } + } + } - #[allow(dead_code)] - pub fn pop(&mut self) -> Option { - if self.len == 0 { - None - } else { - self.len -= 1; - unsafe { - Some(ptr::read(self.ptr().offset(self.len as isize))) - } + #[allow(dead_code)] + pub fn append(&mut self, other: &mut Vec) { + let mut i: usize = 0; + let olen = other.len(); + let mut drain = Vec::new(); + while i < olen { + drain.push(other.pop().unwrap()); + i += 1; + } + // Empty other + i = 0; + while i < olen { + self.push(drain.pop().unwrap()); + i += 1; + } } - } - - #[allow(dead_code)] - pub fn append(&mut self, other: &mut Vec) { - let mut i: usize = 0; - let olen = other.len(); - let mut drain = Vec::new(); - while i < olen { - drain.push(other.pop().unwrap()); - i += 1; - } - // Empty other - i = 0; - while i < olen { - self.push(drain.pop().unwrap()); - i += 1; - } - } - - #[allow(dead_code)] - pub fn insert(&mut self, index: usize, elem: T) { - assert!(index <= self.len); - if self.cap() == self.len { self.buf.grow(); } - unsafe { - if index < self.len { - ptr::copy(self.ptr().offset(index as isize), - self.ptr().offset(index as isize + 1), - self.len - index); - } - ptr::write(self.ptr().offset(index as isize), elem); - self.len += 1; + #[allow(dead_code)] + pub fn insert(&mut self, index: usize, elem: T) { + assert!(index <= self.len); + if self.cap() == self.len { + self.buf.grow(); + } + + unsafe { + if index < self.len { + ptr::copy( + self.ptr().offset(index as isize), + self.ptr().offset(index as isize + 1), + self.len - index, + ); + } + ptr::write(self.ptr().offset(index as isize), elem); + self.len += 1; + } } - } - #[allow(dead_code)] - pub fn remove(&mut self, index: usize) -> T { - assert!(index < self.len); - unsafe { - self.len -= 1; - let result = ptr::read(self.ptr().offset(index as isize)); - ptr::copy(self.ptr().offset(index as isize + 1), + #[allow(dead_code)] + pub fn remove(&mut self, index: usize) -> T { + assert!(index < self.len); + unsafe { + self.len -= 1; + let result = ptr::read(self.ptr().offset(index as isize)); + ptr::copy( + self.ptr().offset(index as isize + 1), self.ptr().offset(index as isize), - self.len - index); - result + self.len - index, + ); + result + } } - } - - #[allow(dead_code)] - pub fn into_iter(self) -> IntoIter { - unsafe { - let iter = RawValIter::new(&self); - let buf = ptr::read(&self.buf); - mem::forget(self); - IntoIter { - iter: iter, - _buf: buf, - } + #[allow(dead_code)] + pub fn into_iter(self) -> IntoIter { + unsafe { + let iter = RawValIter::new(&self); + let buf = ptr::read(&self.buf); + mem::forget(self); + + IntoIter { + iter: iter, + _buf: buf, + } + } + } + #[allow(dead_code)] + pub fn len(&self) -> usize { + self.len } - } - #[allow(dead_code)] - pub fn len(&self) -> usize { - self.len - } } #[cfg(verifier = "smack")] -impl Default for Vec { - fn default() -> Self { - Vec::new() - } +impl Default for Vec { + fn default() -> Self { + Vec::new() + } } #[cfg(verifier = "smack")] impl Drop for Vec { - fn drop(&mut self) { - while let Some(_) = self.pop() {} - // allocation is handled by RawVec - } + fn drop(&mut self) { + while let Some(_) = self.pop() {} + // allocation is handled by RawVec + } } #[cfg(verifier = "smack")] impl Deref for Vec { - type Target = [T]; - fn deref(&self) -> &[T] { - unsafe { - ::std::slice::from_raw_parts(self.buf.ptr.ptr, self.len) + type Target = [T]; + fn deref(&self) -> &[T] { + unsafe { ::std::slice::from_raw_parts(self.buf.ptr.ptr, self.len) } } - } } #[cfg(verifier = "smack")] impl DerefMut for Vec { - fn deref_mut(&mut self) -> &mut [T] { - unsafe { - ::std::slice::from_raw_parts_mut(self.buf.ptr.ptr as *mut T, self.len) + fn deref_mut(&mut self) -> &mut [T] { + unsafe { ::std::slice::from_raw_parts_mut(self.buf.ptr.ptr as *mut T, self.len) } } - } } #[cfg(verifier = "smack")] struct RawValIter { - start: *const T, - end: *const T, + start: *const T, + end: *const T, } #[cfg(verifier = "smack")] impl RawValIter { - unsafe fn new(slice: &[T]) -> Self { - RawValIter { - start: slice.as_ptr(), - end: if mem::size_of::() == 0 { - ((slice.as_ptr() as usize) + slice.len()) as *const _ - } else if slice.len() == 0 { - slice.as_ptr() - } else { - slice.as_ptr().offset(slice.len() as isize) - } + unsafe fn new(slice: &[T]) -> Self { + RawValIter { + start: slice.as_ptr(), + end: if mem::size_of::() == 0 { + ((slice.as_ptr() as usize) + slice.len()) as *const _ + } else if slice.len() == 0 { + slice.as_ptr() + } else { + slice.as_ptr().offset(slice.len() as isize) + }, + } } - } } #[cfg(verifier = "smack")] impl Iterator for RawValIter { - type Item = T; - fn next(&mut self) -> Option { - if self.start == self.end { - None - } else { - unsafe { - let result = ptr::read(self.start); - self.start = if mem::size_of::() == 0 { - (self.start as usize + 1) as *const _ + type Item = T; + fn next(&mut self) -> Option { + if self.start == self.end { + None } else { - self.start.offset(1) - }; - Some(result) - } + unsafe { + let result = ptr::read(self.start); + self.start = if mem::size_of::() == 0 { + (self.start as usize + 1) as *const _ + } else { + self.start.offset(1) + }; + Some(result) + } + } } - } - fn size_hint(&self) -> (usize, Option) { - let elem_size = mem::size_of::(); - let len = (self.end as usize - self.start as usize) - / if elem_size == 0 { 1 } else { elem_size }; - (len, Some(len)) - } + fn size_hint(&self) -> (usize, Option) { + let elem_size = mem::size_of::(); + let len = + (self.end as usize - self.start as usize) / if elem_size == 0 { 1 } else { elem_size }; + (len, Some(len)) + } } #[cfg(verifier = "smack")] impl DoubleEndedIterator for RawValIter { - fn next_back(&mut self) -> Option { - if self.start == self.end { - None - } else { - unsafe { - self.end = if mem::size_of::() == 0 { - (self.end as usize - 1) as *const _ + fn next_back(&mut self) -> Option { + if self.start == self.end { + None } else { - self.end.offset(-1) - }; - Some(ptr::read(self.end)) - } + unsafe { + self.end = if mem::size_of::() == 0 { + (self.end as usize - 1) as *const _ + } else { + self.end.offset(-1) + }; + Some(ptr::read(self.end)) + } + } } - } } #[cfg(verifier = "smack")] pub struct IntoIter { - _buf: RawVec, // we don't actually care about this. Just need it to live. - iter: RawValIter, + _buf: RawVec, // we don't actually care about this. Just need it to live. + iter: RawValIter, } #[cfg(verifier = "smack")] impl Iterator for IntoIter { - type Item = T; - fn next(&mut self) -> Option { self.iter.next() } - fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } + type Item = T; + fn next(&mut self) -> Option { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } } #[cfg(verifier = "smack")] impl DoubleEndedIterator for IntoIter { - fn next_back(&mut self) -> Option { self.iter.next_back() } + fn next_back(&mut self) -> Option { + self.iter.next_back() + } } #[cfg(verifier = "smack")] impl Drop for IntoIter { - fn drop(&mut self) { - for _ in &mut *self {} - } + fn drop(&mut self) { + for _ in &mut *self {} + } } #[cfg(verifier = "smack")] @@ -452,22 +521,23 @@ macro_rules! vec { )* result } - }; + }; } #[cfg(verifier = "smack")] #[allow(dead_code)] pub struct Box { - ptr: Unique + ptr: Unique, } #[cfg(verifier = "smack")] #[allow(dead_code)] -impl Box { pub fn new(item: T) -> Box { +impl Box { + pub fn new(item: T) -> Box { let elem_size = mem::size_of::(); let ptr = unsafe { Unique::new(malloc(elem_size) as *mut T) }; - unsafe{ ptr::write(ptr.as_ptr().offset(0), item) }; - Box {ptr: ptr} + unsafe { ptr::write(ptr.as_ptr().offset(0), item) }; + Box { ptr: ptr } } } @@ -476,6 +546,6 @@ impl Box { pub fn new(item: T) -> Box { impl Deref for Box { type Target = T; fn deref(&self) -> &Self::Target { - unsafe{ mem::transmute::<*mut T, &T>(self.ptr.as_ptr()) } + unsafe { mem::transmute::<*mut T, &T>(self.ptr.as_ptr()) } } } diff --git a/share/smack/lib/stdlib.c b/share/smack/lib/stdlib.c index eb98f7e3f..99a12cc90 100644 --- a/share/smack/lib/stdlib.c +++ b/share/smack/lib/stdlib.c @@ -7,7 +7,16 @@ void exit(int x) { #if MEMORY_SAFETY - __SMACK_code("assert $allocatedCounter == 0;"); + __SMACK_code("assert {:valid_memtrack} $allocatedCounter == 0;"); +#endif + __SMACK_code("assume false;"); + while (1) + ; +} + +void abort(void) { +#if MEMORY_SAFETY + __SMACK_code("assert {:valid_memtrack} $allocatedCounter == 0;"); #endif __SMACK_code("assume false;"); while (1) diff --git a/share/smack/reach.py b/share/smack/reach.py index 750667ea5..3aef8c852 100755 --- a/share/smack/reach.py +++ b/share/smack/reach.py @@ -11,7 +11,8 @@ from smackgen import * from smackverify import * -VERSION = '2.4.1' +VERSION = '2.5.0' + def reachParser(): parser = argparse.ArgumentParser(add_help=False, parents=[verifyParser()]) @@ -19,6 +20,8 @@ def reachParser(): return parser # File line numbers are 0-based idx + + def CopyFileWhileInserting(srcFile, dstFile, lineNumber, insertText): inFile = open(srcFile, "r") inContents = inFile.readlines() @@ -34,60 +37,82 @@ def CopyFileWhileInserting(srcFile, dstFile, lineNumber, insertText): outFile.close() # Each item in return value is [fileName, sourceLineNo, bplLineNo, isReachable] + + def GetSourceLineInfo(bplFile): - FILENAME = '[\w#$~%.\/-]+' - regex = '.*{:sourceloc \"(' + FILENAME + ')\", (\d+), (\d+)}.*' + FILENAME = r'[\w#$~%.\/-]+' + regex = '.*{:sourceloc \"(' + FILENAME + r')\", (\d+), (\d+)}.*' sourcelocRe = re.compile(regex) sourceInfo = [] - bplCount = 0; + bplCount = 0 with open(bplFile) as inFile: for line in inFile.readlines(): - #Groups: 1=filename, 2=sourceLineNo, 3=sourceColNo + # Groups: 1=filename, 2=sourceLineNo, 3=sourceColNo match = sourcelocRe.match(line) if(match): newSource = { - 'filename' : match.group(1), - 'sourceLineNo' : int(match.group(2)), - 'sourceColNo' : int(match.group(3)), - 'bplLineNo' : bplCount, - 'isReachable' : False - } + 'filename': match.group(1), + 'sourceLineNo': int(match.group(2)), + 'sourceColNo': int(match.group(3)), + 'bplLineNo': bplCount, + 'isReachable': False + } sourceInfo.append(newSource) bplCount += 1 - return sorted(sourceInfo, key=lambda e:e['sourceLineNo'], reverse=True) + return sorted(sourceInfo, key=lambda e: e['sourceLineNo'], reverse=True) + def UpdateWithClangInfo(clangOuptut, sourceInfo): - FILENAME = '[\w#$~%.\/-]+' - regex = '(' + FILENAME + '):(\d+):(\d+): warning: will never be executed \[-Wunreachable-code\]' + FILENAME = r'[\w#$~%.\/-]+' + regex = ('(' + + FILENAME + + (r'):(\d+):(\d+): warning: will never be executed ' + r'\[-Wunreachable-code\]')) clangFilter = re.compile(regex) for line in clangOutput.splitlines(True): match = clangFilter.match(line) if(match): newSource = { - 'filename' : match.group(1), - 'sourceLineNo' : int(match.group(2)), - 'sourceColNo' : int(match.group(3)), - 'bplLineNo' : -1, - 'isReachable' : False - } + 'filename': match.group(1), + 'sourceLineNo': int(match.group(2)), + 'sourceColNo': int(match.group(3)), + 'bplLineNo': -1, + 'isReachable': False + } sourceInfo.append(newSource) -def GetCodeCoverage(verifier, bplFileName, timeLimit, unroll, contextSwitches, debug, smackd, clangOutput): + +def GetCodeCoverage( + verifier, + bplFileName, + timeLimit, + unroll, + contextSwitches, + debug, + smackd, + clangOutput): sourceInfo = GetSourceLineInfo(bplFileName) for sourceLine in sourceInfo: if(not sourceLine['isReachable']): - reachRes = TestReachability(verifier, bplFileName, timeLimit, unroll, contextSwitches, debug, sourceLine) - - #TODO - how does python handle changing lists in for loop? + reachRes = TestReachability( + verifier, + bplFileName, + timeLimit, + unroll, + contextSwitches, + debug, + sourceLine) + + # TODO - how does python handle changing lists in for loop? UpdateSourceInfo(reachRes, sourceInfo, verifier) - #Add lines caught by clang's -Wunreachable-code + # Add lines caught by clang's -Wunreachable-code UpdateWithClangInfo(clangOutput, sourceInfo) # Extract info @@ -100,10 +125,16 @@ def GetCodeCoverage(verifier, bplFileName, timeLimit, unroll, contextSwitches, d result[sourceLine["filename"]].add(resItem) for curfile in result: - #secondary sort by column - result[curfile] = sorted(result[curfile], key=lambda e:e[1], reverse=False) - #primary sort by line - result[curfile] = sorted(result[curfile], key=lambda e:e[0], reverse=False) + # secondary sort by column + result[curfile] = sorted( + result[curfile], + key=lambda e: e[1], + reverse=False) + # primary sort by line + result[curfile] = sorted( + result[curfile], + key=lambda e: e[0], + reverse=False) if(smackd): print((json.dumps(result))) @@ -112,59 +143,84 @@ def GetCodeCoverage(verifier, bplFileName, timeLimit, unroll, contextSwitches, d print("Unreachable code:") pprint.pprint(result, width=100) -def TestReachability(verifier, bplFileName, timeLimit, unroll, contextSwitches, debug, lineInfo): + +def TestReachability( + verifier, + bplFileName, + timeLimit, + unroll, + contextSwitches, + debug, + lineInfo): boogieText = "assert false;" bplfileBase = path.splitext(bplFileName)[0] bplNew = bplfileBase + "_coverage.bpl" - CopyFileWhileInserting(bplFileName, bplNew, lineInfo['bplLineNo'] + 1, boogieText) - - #do not pass smackd flag as true. Breaks parsing - corralOutput = verify(verifier, bplNew, timeLimit, unroll, contextSwitches, debug, False) + CopyFileWhileInserting( + bplFileName, + bplNew, + lineInfo['bplLineNo'] + 1, + boogieText) + + # do not pass smackd flag as true. Breaks parsing + corralOutput = verify( + verifier, + bplNew, + timeLimit, + unroll, + contextSwitches, + debug, + False) return corralOutput + def UpdateSourceInfo(corralOutput, sourceInfo, verifier): - FILENAME = '[\w#$~%.\/-]+' + FILENAME = r'[\w#$~%.\/-]+' regex = "" if(verifier == "corral"): - regex = '(' + FILENAME + ')\((\d+),(\d+)\): Trace:.*' + regex = '(' + FILENAME + r')\((\d+),(\d+)\): Trace:.*' else: - #boogie... - #TODO Once line numbers are fixed for boogie-inline, + # boogie... + # TODO Once line numbers are fixed for boogie-inline, # we can save time here by using all traces, # not just the instruction causing the failure # (use trace that led to failing instruction, also) # As is, current smackverify output using boogie-inline # is including unvisited lines in traces. (See # src/test/reach/switch.c) - regex = '\s*(' + FILENAME + ')\((\d+),(\d+)\): Error.*' + regex = r'\s*(' + FILENAME + r')\((\d+),(\d+)\): Error.*' traceFilter = re.compile(regex) for line in corralOutput.splitlines(True): match = traceFilter.match(line) if(match): reachedLine = { - 'filename' : match.group(1), - 'sourceLineNo' : int(match.group(2)), + 'filename': match.group(1), + 'sourceLineNo': int(match.group(2)), # Corral adds one to column count - 'sourceColNo' : int(match.group(3)), - } - #run through each sourceInfo, if matches, set as reachable + 'sourceColNo': int(match.group(3)), + } + # run through each sourceInfo, if matches, set as reachable for sourceLine in sourceInfo: - if ((not sourceLine['isReachable']) and + if ((not sourceLine['isReachable']) and reachedLine['filename'] == sourceLine['filename'] and - reachedLine['sourceLineNo'] == sourceLine['sourceLineNo'] and - reachedLine['sourceColNo'] == sourceLine['sourceColNo']): + (reachedLine['sourceLineNo'] == + sourceLine['sourceLineNo']) and + (reachedLine['sourceColNo'] == + sourceLine['sourceColNo'])): sourceLine['isReachable'] = True def main(): - parser = argparse.ArgumentParser(description='Checks the input LLVM file for code reachability.', parents=[reachParser()]) - parser.parse_args() # just check if arguments are looking good + parser = argparse.ArgumentParser( + description='Checks the input LLVM file for code reachability.', + parents=[ + reachParser()]) + parser.parse_args() # just check if arguments are looking good - #!!!!!!START COPY OF SECTION FROM smackverify.py!!!!!!!!!!! + # !!!!!!START COPY OF SECTION FROM smackverify.py!!!!!!!!!!! # Probably should pull into subroutine or something # remove arguments not recognized by lower scripts # not sure of a better way to do this @@ -179,8 +235,7 @@ def main(): del sysArgv[i] del sysArgv[i] - - #Add clang's -Wunreachable-code flag + # Add clang's -Wunreachable-code flag sysArgv.append('--clang=-Wunreachable-code') bpl, options, clangOutput = smackGenerate(sysArgv) args = parser.parse_args(options + sys.argv[1:]) @@ -188,6 +243,14 @@ def main(): # write final output args.outfile.write(bpl) args.outfile.close() - #!!!!!!END COPY OF SECTION FROM smackverify.py!!!!!!!!!!! - - GetCodeCoverage(args.verifier, args.outfile.name, args.timeLimit, args.unroll, args.contextSwitches, args.debug, args.smackd, clangOutput) + # !!!!!!END COPY OF SECTION FROM smackverify.py!!!!!!!!!!! + + GetCodeCoverage( + args.verifier, + args.outfile.name, + args.timeLimit, + args.unroll, + args.contextSwitches, + args.debug, + args.smackd, + clangOutput) diff --git a/share/smack/replay.py b/share/smack/replay.py index 8064eafda..07af545e7 100644 --- a/share/smack/replay.py +++ b/share/smack/replay.py @@ -1,98 +1,104 @@ -import os import re -import subprocess -import sys from .utils import temporary_file, try_command SPECIAL_NAMES = [ - '__VERIFIER_assert', - '__VERIFIER_assume' + '__VERIFIER_assert', + '__VERIFIER_assume' ] + def replay_error_trace(verifier_output, args): - if args.verifier != 'corral': - print("Replay for verifiers other than 'corral' currently unsupported; skipping replay") - return + if args.verifier != 'corral': + print(("Replay for verifiers other than 'corral' currently unsupported" + "; skipping replay")) + return - print("Attempting to replay error trace.") + print("Attempting to replay error trace.") - missing_definitions = detect_missing_definitions(args.bc_file) - if '__SMACK_code' in missing_definitions: - print("warning: inline Boogie code found; replay may fail") + missing_definitions = detect_missing_definitions(args.bc_file) + if '__SMACK_code' in missing_definitions: + print("warning: inline Boogie code found; replay may fail") - arguments, return_values = extract_values(verifier_output) + arguments, return_values = extract_values(verifier_output) - with open(args.replay_harness, 'w') as f: - f.write(harness(arguments, return_values, missing_definitions)) - print("Generated replay harness:", args.replay_harness) + with open(args.replay_harness, 'w') as f: + f.write(harness(arguments, return_values, missing_definitions)) + print("Generated replay harness:", args.replay_harness) - stubs_bc = temporary_file('stubs', '.bc', args) - try_command(['clang', '-c', '-emit-llvm', '-o', stubs_bc, args.replay_harness]) - try_command(['clang', '-Wl,-e,_smack_replay_main', '-o', args.replay_exe_file, args.bc_file, stubs_bc]) - print("Generated replay executable:", args.replay_exe_file) + stubs_bc = temporary_file('stubs', '.bc', args) + try_command(['clang', '-c', '-emit-llvm', '-o', + stubs_bc, args.replay_harness]) + try_command(['clang', '-Wl,-e,_smack_replay_main', '-o', + args.replay_exe_file, args.bc_file, stubs_bc]) + print("Generated replay executable:", args.replay_exe_file) - try: - if 'error reached!' in try_command(["./" + args.replay_exe_file]): - print("Error-trace replay successful.") - return True + try: + if 'error reached!' in try_command(["./" + args.replay_exe_file]): + print("Error-trace replay successful.") + return True - else: - print("Error-trace replay failed.") + else: + print("Error-trace replay failed.") - except Exception as err: - print("Error-trace replay caught", err.message) + except Exception as err: + print("Error-trace replay caught", err) - return False + return False def detect_missing_definitions(bc_file): - missing = [] - try: - try_command(['clang', bc_file]) - except Exception as err: - for line in err.message.split("\n"): - m = re.search(r'\"_(.*)\", referenced from:', line) or re.search(r'undefined reference to `(.*)\'', line) - if m: - missing.append(m.group(1)) - return missing + missing = [] + try: + try_command(['clang', bc_file]) + except Exception as err: + msg = repr(err).replace("\\n", "\n") + for line in msg.split("\n"): + m = re.search( + r'\"_(.*)\", referenced from:', + line) or re.search( + r'undefined reference to `(.*)\'', + line) + if m: + missing.append(m.group(1)) + return missing def extract(line): - match = re.search(r'.*\((smack:.*) = (.*)\).*', line) - return match and [match.group(1), match.group(2)] + match = re.search(r'.*\((smack:.*) = (.*)\).*', line) + return match and [match.group(1), match.group(2)] def extract_values(trace): - arguments = {} - return_values = {} + arguments = {} + return_values = {} - for key, val in [x for x in map(extract, trace.split('\n')) if x]: - if 'smack:entry:' in key: - _, _, fn = key.split(':') - arguments[fn] = [] + for key, val in [x for x in map(extract, trace.split('\n')) if x]: + if 'smack:entry:' in key: + _, _, fn = key.split(':') + arguments[fn] = [] - elif 'smack:arg:' in key: - _, _, fn, arg = key.split(':') - if not fn in arguments: - raise Exception("expected entry point key smack:entry:%s" % fn) - arguments[fn].append(val) + elif 'smack:arg:' in key: + _, _, fn, arg = key.split(':') + if fn not in arguments: + raise Exception("expected entry point key smack:entry:%s" % fn) + arguments[fn].append(val) - elif 'smack:ext:' in key: - _, _, fn = key.split(':') - if not fn in return_values: - return_values[fn] = [] - return_values[fn].append(val) + elif 'smack:ext:' in key: + _, _, fn = key.split(':') + if fn not in return_values: + return_values[fn] = [] + return_values[fn].append(val) - else: - print("warning: unexpected key %s" % key) + else: + print("warning: unexpected key %s" % key) - return arguments, return_values + return arguments, return_values def harness(arguments, return_values, missing_definitions): - code = [] - code.append("""// + code = [] + code.append("""// // This file was automatically generated from a Boogie error trace. // It contains stubs for unspecified functions that were called in that trace. // These stubs will return the same values they did in the error trace. @@ -116,9 +122,9 @@ def harness(arguments, return_values, missing_definitions): } """) - for fn in set(missing_definitions) - set(SPECIAL_NAMES): - if fn in return_values: - code.append("""// stub for function: %(fn)s + for fn in set(missing_definitions) - set(SPECIAL_NAMES): + if fn in return_values: + code.append("""// stub for function: %(fn)s int %(fn)s$table[] = {%(vals)s}; int %(fn)s$idx = 0; @@ -127,22 +133,22 @@ def harness(arguments, return_values, missing_definitions): } """ % {'fn': fn, 'vals': ", ".join(return_values[fn])}) - else: - print("warning: unknown return value for %s" % fn) - code.append("""// stub for function %(fn)s + else: + print("warning: unknown return value for %s" % fn) + code.append("""// stub for function %(fn)s void %(fn)s() { return; } """ % {'fn': fn}) - if len(arguments) > 1: - print("warning: multiple entrypoint argument annotations found") + if len(arguments) > 1: + print("warning: multiple entrypoint argument annotations found") - elif len(arguments) < 1: - print("warning: no entrypoint argument annotations found") + elif len(arguments) < 1: + print("warning: no entrypoint argument annotations found") - for fn, args in list(arguments.items()): - code.append("""// entry point wrapper + for fn, args in list(arguments.items()): + code.append("""// entry point wrapper int _smack_replay_main() { %(fn)s(%(vals)s); return 0; @@ -153,4 +159,4 @@ def harness(arguments, return_values, missing_definitions): } """ % {'fn': fn, 'vals': ", ".join(args)}) - return "\n".join(code) + return "\n".join(code) diff --git a/share/smack/svcomp/random_testing.py b/share/smack/svcomp/random_testing.py index 18430c7c1..fa9d09aff 100644 --- a/share/smack/svcomp/random_testing.py +++ b/share/smack/svcomp/random_testing.py @@ -26,9 +26,9 @@ def random_test(args, result): def compile_and_run(f, s, n, args): s = re.sub("=\s*(__VERIFIER_nondet_uint\(\))", "="+str(n), s) s = re.sub("__VERIFIER_assert\((.+)\);", "assert(\\1);", s) - s = re.sub(r'(extern )?void __VERIFIER_error()', '//', s) + s = re.sub(r'(extern )?void reach_error()', '//', s) s = re.sub(r'(extern )?void __VERIFIER_assume()', '//', s) - s = re.sub(r'__VERIFIER_error\(\)', 'assert(0)', s) + s = re.sub(r'reach_error\(\)', 'assert(0)', s) s = '#include\n' + s name = os.path.splitext(os.path.basename(f))[0] diff --git a/share/smack/svcomp/utils.py b/share/smack/svcomp/utils.py index 370c5d764..c520e6bca 100644 --- a/share/smack/svcomp/utils.py +++ b/share/smack/svcomp/utils.py @@ -29,22 +29,22 @@ def svcomp_frontend(input_file, args): # test bv and executable benchmarks file_type, executable = filters.svcomp_filter(args.input_files[0]) if file_type == 'bitvector': - args.bit_precise = True - args.bit_precise_pointers = True + args.integer_encoding = 'bit-vector' + args.pointer_encoding = 'bit-vector' if file_type == 'float' and not args.integer_overflow: args.float = True - args.bit_precise = True + args.integer_encoding = 'bit-vector' with open(input_file, "r") as sf: sc = sf.read() if 'copysign(1' in sc: - args.bit_precise_pointers = True + args.pointer_encoding = 'bit-vector' args.execute = executable else: with open(input_file, "r") as sf: sc = sf.read() if "unsigned char b:2" in sc or "4294967294u" in sc or "_ddv_module_init" in sc or "bb_process_escape_sequence" in sc: - args.bit_precise = True - #args.bit_precise_pointers = True + args.integer_encoding = 'bit-vector' + #args.pointer_encoding = 'bit-vector' name, ext = os.path.splitext(os.path.basename(args.input_files[0])) svcomp_process_file(args, name, ext) @@ -82,7 +82,7 @@ def svcomp_check_property(args): args.only_check_memcleanup = True elif "overflow" in prop: args.integer_overflow = True - elif not "__VERIFIER_error" in prop: + elif not "reach_error" in prop: sys.exit(smack.top.results(args)['unknown']) def svcomp_process_file(args, name, ext): @@ -112,10 +112,10 @@ def svcomp_process_file(args, name, ext): pass if 'argv=malloc' in s: -# args.bit_precise = True +# args.integer_encoding = 'bit-vector' if args.integer_overflow and ('unsigned int d = (unsigned int)((signed int)(unsigned char)((signed int)*q | (signed int)(char)32) - 48);' in s or 'bb_ascii_isalnum' in s or 'ptm=localtime' in s or '0123456789.' in s): - args.bit_precise = True - args.bit_precise_pointers = True + args.integer_encoding = 'bit-vector' + args.pointer_encoding = 'bit-vector' length = len(s.split('\n')) if length < 60: @@ -169,10 +169,20 @@ def is_stack_benchmark(args, csource): print("Stumbled upon a stack-based memory safety benchmark\n") sys.exit(smack.top.results(args)['unknown']) +def inject_assert_false(args): + with open(args.bpl_file, 'r') as bf: + content = bf.read() + content = content.replace('call reach_error();', 'assert false; call reach_error();') + with open(args.bpl_file, 'w') as bf: + bf.write(content) + def verify_bpl_svcomp(args): """Verify the Boogie source file using SVCOMP-tuned heuristics.""" heurTrace = "\n\nHeuristics Info:\n" + if not args.memory_safety and not args.only_check_memcleanup and not args.integer_overflow: + inject_assert_false(args) + if args.memory_safety: if not (args.only_check_valid_deref or args.only_check_valid_free or args.only_check_memleak): heurTrace = "engage valid deference checks.\n" @@ -216,6 +226,7 @@ def verify_bpl_svcomp(args): corral_command += [args.bpl_file] corral_command += ["/tryCTrace", "/noTraceOnDisk", "/printDataValues:1"] corral_command += ["/useProverEvaluate", "/cex:1"] + corral_command += ["/bopt:proverOpt:O:smt.qi.eager_threshold=100"] with open(args.bpl_file, "r") as f: bpl = f.read() @@ -246,7 +257,7 @@ def verify_bpl_svcomp(args): corral_command += ["/cooperative"] else: corral_command += ["/k:1"] - if not (args.memory_safety or args.bit_precise or args.only_check_memcleanup): + if not (args.memory_safety or args.integer_encoding == 'bit-vector' or args.only_check_memcleanup): if not ("dll_create" in csource or "sll_create" in csource or "changeMethaneLevel" in csource): corral_command += ["/di"] @@ -260,7 +271,7 @@ def verify_bpl_svcomp(args): # Setting good loop unroll bound based on benchmark class loopUnrollBar = 8 staticLoopBound = 65536 - if not args.bit_precise and "ssl3_accept" in bpl and "s__s3__tmp__new_cipher__algorithms" in bpl: + if not args.integer_encoding == 'bit-vector' and "ssl3_accept" in bpl and "s__s3__tmp__new_cipher__algorithms" in bpl: heurTrace += "ControlFlow benchmark detected. Setting loop unroll bar to 23.\n" loopUnrollBar = 23 elif "s3_srvr.blast.10_false-unreach-call" in bpl or "s3_srvr.blast.15_false-unreach-call" in bpl: @@ -320,12 +331,7 @@ def verify_bpl_svcomp(args): if not "forall" in bpl: heurTrace += "No quantifiers detected. Setting z3 relevancy to 0.\n" - corral_command += ["/bopt:z3opt:smt.relevancy=0"] - - if args.bit_precise: - heurTrace += "--bit-precise flag passed - enabling bit vectors mode.\n" - corral_command += ["/bopt:proverOpt:OPTIMIZE_FOR_BV=true"] - corral_command += ["/bopt:boolControlVC"] + corral_command += ["/bopt:proverOpt:O:smt.relevancy=0"] if args.memory_safety: if args.prop_to_check == 'valid-deref': @@ -486,8 +492,8 @@ def run_binary(args): with open(args.input_files[0], 'r') as fi: s = fi.read() - s = re.sub(r'(extern )?void __VERIFIER_error()', '//', s) - s = re.sub(r'__VERIFIER_error\(\)', 'assert(0)', s) + s = re.sub(r'(extern )?void reach_error()', '//', s) + s = re.sub(r'reach_error\(\)', 'assert(0)', s) s = '#include\n' + s name = os.path.splitext(os.path.basename(args.input_files[0]))[0] diff --git a/share/smack/top.py b/share/smack/top.py index 021437c0e..49ddef237 100755 --- a/share/smack/top.py +++ b/share/smack/top.py @@ -1,694 +1,985 @@ import argparse -import errno -import io import json import os -import platform import re import shutil import sys import shlex import subprocess import signal +import functools from .svcomp.utils import verify_bpl_svcomp from .utils import temporary_file, try_command, remove_temp_files from .replay import replay_error_trace -from .frontend import link_bc_files, frontends, languages, extra_libs +from .frontend import link_bc_files, frontends, languages, extra_libs -VERSION = '2.4.1' +VERSION = '2.5.0' -def results(args): - """A dictionary of the result output messages.""" - return { - 'verified': 'SMACK found no errors.' if args.modular else 'SMACK found no errors with unroll bound %s.' % args.unroll, - 'error': 'SMACK found an error.', - 'invalid-deref': 'SMACK found an error: invalid pointer dereference.', - 'invalid-free': 'SMACK found an error: invalid memory deallocation.', - 'invalid-memtrack': 'SMACK found an error: memory leak.', - 'overflow': 'SMACK found an error: integer overflow.', - 'timeout': 'SMACK timed out.', - 'unknown': 'SMACK result is unknown.' - } - -def inlined_procedures(): - return [ - '$galloc', - '$alloc', - '$malloc', - '$free', - '$memset', - '$memcpy', - '__VERIFIER_', - '$initialize', - '__SMACK_static_init', - '__SMACK_init_func_memory_model', - '__SMACK_check_overflow' - ] -class FileAction(argparse.Action): - def __init__(self, option_strings, dest, **kwargs): - super(FileAction, self).__init__(option_strings, dest, **kwargs) - def __call__(self, parser, namespace, values, option_string=None): - if option_string is None: - validate_input_files(values) - else: - # presumably output files (e.g., .bc, .ll, etc) - validate_output_file(values) - setattr(namespace, self.dest, values) +def results(args): + """A dictionary of the result output messages.""" + return { + 'verified': ('SMACK found no errors' + + ('' if args.modular else + ' with unroll bound %s' % args.unroll) + '.', 0), + 'error': ('SMACK found an error.', 1), + 'invalid-deref': ('SMACK found an error: invalid pointer dereference.', + 2), + 'invalid-free': ('SMACK found an error: invalid memory deallocation.', + 3), + 'invalid-memtrack': ('SMACK found an error: memory leak.', 4), + 'overflow': ('SMACK found an error: integer overflow.', 5), + 'rust-panic': ('SMACK found an error: Rust panic.', 6), + 'timeout': ('SMACK timed out.', 126), + 'unknown': ('SMACK result is unknown.', 127)} -def exit_with_error(error): - sys.exit('Error: %s.' % error) -def validate_input_files(files): - def validate_input_file(file): - """Check whether the given input file is valid, returning a reason if not.""" +def inlined_procedures(): + return [ + '$galloc', + '$alloc', + '$malloc', + '$free', + '$memset', + '$memcpy', + '__VERIFIER_', + '$initialize', + '__SMACK_static_init', + '__SMACK_init_func_memory_model', + '__SMACK_check_overflow' + ] - file_extension = os.path.splitext(file)[1][1:] - if not os.path.isfile(file): - exit_with_error("Cannot find file %s" % file) - if not os.access(file, os.R_OK): - exit_with_error("Cannot read file %s" % file) +class FileAction(argparse.Action): + def __init__(self, option_strings, dest, **kwargs): + super(FileAction, self).__init__(option_strings, dest, **kwargs) - elif not file_extension in languages(): - exit_with_error("Unexpected source file extension '%s'" % file_extension) - list(map(validate_input_file, files)) + def __call__(self, parser, namespace, values, option_string=None): + if option_string is None: + validate_input_files(values) + else: + # presumably output files (e.g., .bc, .ll, etc) + validate_output_file(values) + setattr(namespace, self.dest, values) -def validate_output_file(file): - dir_name = os.path.dirname(os.path.abspath(file)) - if not os.path.isdir(dir_name): - exit_with_error("directory %s doesn't exist" % dirname) - if not os.access(dir_name, os.W_OK): - exit_with_error("file %s may not be writeable" % file) - #try: - # with open(file, 'w') as f: - # pass - #except IOError: - # exit_with_error("file %s may not be writeable" % file) -def arguments(): - """Parse command-line arguments""" +def exit_with_error(error): + sys.exit('Error: %s.' % error) - parser = argparse.ArgumentParser() - parser.add_argument('input_files', metavar='input-files', nargs='+', action=FileAction, - type = str, help = 'source file to be translated/verified') +def validate_input_files(files): + def validate_input_file(file): + """ + Check whether the given input file is valid, returning a reason if not. + """ - parser.add_argument('--version', action='version', - version='SMACK version ' + VERSION) + file_extension = os.path.splitext(file)[1][1:] + if not os.path.isfile(file): + exit_with_error("Cannot find file %s" % file) - noise_group = parser.add_mutually_exclusive_group() + if not os.access(file, os.R_OK): + exit_with_error("Cannot read file %s" % file) - noise_group.add_argument('-q', '--quiet', action='store_true', default=False, - help='enable quiet output') + elif file_extension not in languages(): + exit_with_error( + "Unexpected source file extension '%s'" % + file_extension) + list(map(validate_input_file, files)) - noise_group.add_argument('-v', '--verbose', action='store_true', default=False, - help='enable verbose output') - noise_group.add_argument('-d', '--debug', action="store_true", default=False, - help='enable debugging output') +def validate_output_file(file): + dir_name = os.path.dirname(os.path.abspath(file)) + if not os.path.isdir(dir_name): + exit_with_error("directory %s doesn't exist" % dir_name) + if not os.access(dir_name, os.W_OK): + exit_with_error("file %s may not be writeable" % file) + # try: + # with open(file, 'w') as f: + # pass + # except IOError: + # exit_with_error("file %s may not be writeable" % file) - noise_group.add_argument('--debug-only', metavar='MODULES', default=None, - type=str, help='limit debugging output to given MODULES') - noise_group.add_argument('--warn', default="unsound", - choices=['silent', 'unsound', 'info'], - help='''enable certain type of warning messages +def arguments(): + """Parse command-line arguments""" + + parser = argparse.ArgumentParser() + + parser.add_argument( + 'input_files', + metavar='input-files', + nargs='+', + action=FileAction, + type=str, + help='source file to be translated/verified') + + parser.add_argument('--version', action='version', + version='SMACK version ' + VERSION) + + noise_group = parser.add_mutually_exclusive_group() + + noise_group.add_argument( + '-q', + '--quiet', + action='store_true', + default=False, + help='enable quiet output') + + noise_group.add_argument( + '-v', + '--verbose', + action='store_true', + default=False, + help='enable verbose output') + + noise_group.add_argument( + '-d', + '--debug', + action="store_true", + default=False, + help='enable debugging output') + + noise_group.add_argument( + '--debug-only', + metavar='MODULES', + default=None, + type=str, + help='limit debugging output to given MODULES') + + noise_group.add_argument('--warn', default="unsound", + choices=['silent', 'unsound', 'info'], + help='''enable certain type of warning messages (silent: no warning messages; unsound: warnings about unsoundness; - info: warnings about unsoundness and translation information) [default: %(default)s]''') - - parser.add_argument('-t', '--no-verify', action="store_true", default=False, - help='perform only translation, without verification.') - - parser.add_argument('-w', '--error-file', metavar='FILE', default=None, - type=str, help='save error trace/witness to FILE') - - - frontend_group = parser.add_argument_group('front-end options') - - frontend_group.add_argument('-x', '--language', metavar='LANG', - choices=list(frontends().keys()), default=None, - help='Treat input files as having type LANG.') - - frontend_group.add_argument('-bc', '--bc-file', metavar='FILE', default=None, action=FileAction, - type=str, help='save initial LLVM bitcode to FILE') - - frontend_group.add_argument('--linked-bc-file', metavar='FILE', default=None, - type=str, help=argparse.SUPPRESS) - - frontend_group.add_argument('--replay-harness', metavar='FILE', default='replay-harness.c', - type=str, help=argparse.SUPPRESS) - - frontend_group.add_argument('--replay-exe-file', metavar='FILE', default='replay-exe', - type=str, help=argparse.SUPPRESS) - - frontend_group.add_argument('-ll', '--ll-file', metavar='FILE', default=None, action=FileAction, - type=str, help='save final LLVM IR to FILE') - - frontend_group.add_argument('--clang-options', metavar='OPTIONS', default='', - help='additional compiler arguments (e.g., --clang-options="-w -g")') - - - translate_group = parser.add_argument_group('translation options') - - translate_group.add_argument('-bpl', '--bpl-file', metavar='FILE', default=None, action=FileAction, - type=str, help='save (intermediate) Boogie code to FILE') - - translate_group.add_argument('--no-memory-splitting', action="store_true", default=False, - help='disable region-based memory splitting') - - translate_group.add_argument('--mem-mod', choices=['no-reuse', 'no-reuse-impls', 'reuse'], default='no-reuse-impls', - help='select memory model (no-reuse=never reallocate the same address, reuse=reallocate freed addresses) [default: %(default)s]') - - translate_group.add_argument('--static-unroll', action="store_true", default=False, - help='enable static LLVM loop unrolling pass as a preprocessing step') - - translate_group.add_argument('--pthread', action='store_true', default=False, - help='enable support for pthread programs') - - translate_group.add_argument('--bit-precise', action="store_true", default=False, - help='model non-pointer values as bit vectors') - - translate_group.add_argument('--timing-annotations', action="store_true", default=False, - help='enable timing annotations') - - translate_group.add_argument('--bit-precise-pointers', action="store_true", default=False, - help='model pointers and non-pointer values as bit vectors') - - translate_group.add_argument('--no-byte-access-inference', action="store_true", default=False, - help='disable bit-precision-related optimizations with DSA') - - translate_group.add_argument('--entry-points', metavar='PROC', nargs='+', - default=['main'], help='specify top-level procedures [default: %(default)s]') - - translate_group.add_argument('--memory-safety', action='store_true', default=False, - help='enable memory safety checks') - - translate_group.add_argument('--only-check-valid-deref', action='store_true', default=False, - help='only enable valid dereference checks') - - translate_group.add_argument('--only-check-valid-free', action='store_true', default=False, - help='only enable valid free checks') - - translate_group.add_argument('--only-check-memleak', action='store_true', default=False, - help='only enable memory leak checks') - - translate_group.add_argument('--integer-overflow', action='store_true', default=False, - help='enable integer overflow checks') - - translate_group.add_argument('--llvm-assumes', choices=['none', 'use', 'check'], default='none', - help='optionally enable generation of Boogie assume statements from LLVM assume statements ' + - '(none=no generation [default], use=generate assume statements, check=check assume statements)') - - translate_group.add_argument('--float', action="store_true", default=False, - help='enable bit-precise floating-point functions') + info: warnings about unsoundness and translation information) + [default: %(default)s]''') + + parser.add_argument( + '-t', + '--no-verify', + action="store_true", + default=False, + help='perform only translation, without verification.') + + parser.add_argument('-w', '--error-file', metavar='FILE', default=None, + type=str, help='save error trace/witness to FILE') + + frontend_group = parser.add_argument_group('front-end options') + + frontend_group.add_argument('-x', '--language', metavar='LANG', + choices=list(frontends().keys()), default=None, + help='Treat input files as having type LANG.') + + frontend_group.add_argument( + '-bc', + '--bc-file', + metavar='FILE', + default=None, + action=FileAction, + type=str, + help='save initial LLVM bitcode to FILE') + + frontend_group.add_argument( + '--linked-bc-file', + metavar='FILE', + default=None, + type=str, + help=argparse.SUPPRESS) + + frontend_group.add_argument( + '--replay-harness', + metavar='FILE', + default='replay-harness.c', + type=str, + help=argparse.SUPPRESS) + + frontend_group.add_argument( + '--replay-exe-file', + metavar='FILE', + default='replay-exe', + type=str, + help=argparse.SUPPRESS) + + frontend_group.add_argument( + '-ll', + '--ll-file', + metavar='FILE', + default=None, + action=FileAction, + type=str, + help='save final LLVM IR to FILE') + + frontend_group.add_argument( + '--clang-options', + metavar='OPTIONS', + default='', + help='additional compiler arguments (e.g., --clang-options="-w -g")') + + translate_group = parser.add_argument_group('translation options') + + translate_group.add_argument( + '-bpl', + '--bpl-file', + metavar='FILE', + default=None, + action=FileAction, + type=str, + help='save (intermediate) Boogie code to FILE') + + translate_group.add_argument( + '--no-memory-splitting', + action="store_true", + default=False, + help='disable region-based memory splitting') + + translate_group.add_argument( + '--mem-mod', + choices=[ + 'no-reuse', + 'no-reuse-impls', + 'reuse'], + default='no-reuse-impls', + help='''select memory model + (no-reuse=never reallocate the same address, + reuse=reallocate freed addresses) [default: %(default)s]''') + + translate_group.add_argument( + '--static-unroll', + action="store_true", + default=False, + help='enable static LLVM loop unrolling pass as a preprocessing step') + + translate_group.add_argument( + '--pthread', + action='store_true', + default=False, + help='enable support for pthread programs') + + translate_group.add_argument( + '--max-threads', + default='32', + type=int, + help='bound on the number of threads [default: %(default)s]') + + translate_group.add_argument( + '--integer-encoding', + choices=['bit-vector', 'unbounded-integer', 'wrapped-integer'], + default='unbounded-integer', + help='''machine integer encoding + (bit-vector=use SMT bit-vector theory, + unbounded-integer=use SMT integer theory, + wrapped-integer=use SMT integer theory but model wrap-around + behavior) [default: %(default)s]''') + + translate_group.add_argument( + '--timing-annotations', + action="store_true", + default=False, + help='enable timing annotations') + + translate_group.add_argument( + '--pointer-encoding', + choices=['bit-vector', 'unbounded-integer'], + default='unbounded-integer', + help='''pointer encoding + (bit-vector=use SMT bit-vector theory, + ubounded-integer=use SMT integer theory) + [default: %(default)s]''') + + translate_group.add_argument( + '--no-byte-access-inference', + action="store_true", + default=False, + help='disable bit-precision-related optimizations with DSA') + + translate_group.add_argument( + '--entry-points', + metavar='PROC', + nargs='+', + default=['main'], + help='specify top-level procedures [default: %(default)s]') + + translate_group.add_argument( + '--check', + metavar='PROPERTY', + nargs='+', + choices=['assertions', 'memory-safety', 'valid-deref', 'valid-free', + 'memleak', 'integer-overflow', 'rust-panics'], + default=['assertions'], + help='''select properties to check + [choices: %(choices)s; default: %(default)s] + (note that memory-safety is the union of valid-deref, + valid-free, memleak)''') + + translate_group.add_argument( + '--llvm-assumes', + choices=[ + 'none', + 'use', + 'check'], + default='none', + help='''optionally enable generation of Boogie assume statements from + LLVM assume statements (none=no generation [default], + use=generate assume statements, + check=check assume statements)''') + + translate_group.add_argument( + '--float', + action="store_true", + default=False, + help='enable bit-precise floating-point functions') + + translate_group.add_argument( + '--strings', + action='store_true', + default=False, + help='enable support for string') + + verifier_group = parser.add_argument_group('verifier options') + + verifier_group.add_argument( + '--verifier', + choices=[ + 'boogie', + 'corral', + 'symbooglix', + 'svcomp'], + default='corral', + help='back-end verification engine') + + verifier_group.add_argument('--solver', + choices=['z3', 'cvc4', "yices2"], default='z3', + help='back-end SMT solver') + + verifier_group.add_argument( + '--unroll', + metavar='N', + default='1', + type=lambda x: (int(x) if int(x) > 0 else + parser.error('Unroll bound has to be positive.')), + help='loop/recursion unroll bound [default: %(default)s]') + + verifier_group.add_argument( + '--loop-limit', + metavar='N', + default='1', + type=int, + help='upper bound on minimum loop iterations [default: %(default)s]') + + verifier_group.add_argument( + '--context-bound', + metavar='K', + default='1', + type=int, + help='''bound on the number of thread contexts in Corral + [default: %(default)s]''') + + verifier_group.add_argument( + '--verifier-options', + metavar='OPTIONS', + default='', + help='''additional verifier arguments + (e.g., --verifier-options="/trackAllVars /staticInlining")''') + + verifier_group.add_argument( + '--time-limit', + metavar='N', + default='1200', + type=int, + help='verifier time limit, in seconds [default: %(default)s]') + + verifier_group.add_argument( + '--max-violations', + metavar='N', + default='1', + type=int, + help='maximum reported assertion violations [default: %(default)s]') + + verifier_group.add_argument('--smackd', action="store_true", default=False, + help='generate JSON-format output for SMACKd') + + verifier_group.add_argument( + '--svcomp-property', + metavar='FILE', + default=None, + type=str, + help='load SVCOMP property to check from FILE') + + verifier_group.add_argument( + '--modular', + action="store_true", + default=False, + help='''enable contracts-based modular deductive verification + (uses Boogie)''') + + verifier_group.add_argument( + '--replay', + action="store_true", + default=False, + help='enable replay of error trace with test harness.') + + plugins_group = parser.add_argument_group('plugins') + + plugins_group.add_argument( + '--transform-bpl', + metavar='COMMAND', + default=None, + type=str, + help='transform generated Boogie code via COMMAND') + + plugins_group.add_argument( + '--transform-out', + metavar='COMMAND', + default=None, + type=str, + help='transform verifier output via COMMAND') + + args = parser.parse_args() + + if not args.bc_file: + args.bc_file = temporary_file('a', '.bc', args) + + if not args.linked_bc_file: + args.linked_bc_file = temporary_file('b', '.bc', args) + + if not args.bpl_file: + args.bpl_file = 'a.bpl' if args.no_verify else temporary_file( + 'a', '.bpl', args) + + # TODO are we (still) using this? + # with open(args.input_file, 'r') as f: + # for line in f.readlines(): + # m = re.match('.*SMACK-OPTIONS:[ ]+(.*)$', line) + # if m: + # return args = parser.parse_args(m.group(1).split() + sys.argv[1:]) + + return args - translate_group.add_argument('--strings', action='store_true', default=False, help='enable support for string') - verifier_group = parser.add_argument_group('verifier options') - - verifier_group.add_argument('--verifier', - choices=['boogie', 'corral', 'symbooglix', 'svcomp'], default='corral', - help='back-end verification engine') - - verifier_group.add_argument('--solver', - choices=['z3', 'cvc4'], default='z3', - help='back-end SMT solver') - - verifier_group.add_argument('--unroll', metavar='N', default='1', - type = lambda x: int(x) if int(x) > 0 else parser.error('Unroll bound has to be positive.'), - help='loop/recursion unroll bound [default: %(default)s]') - - verifier_group.add_argument('--loop-limit', metavar='N', default='1', type=int, - help='upper bound on minimum loop iterations [default: %(default)s]') +def target_selection(args): + """Determine the target architecture based on flags and source files.""" + # TODO more possible clang flags that determine the target? + if not re.search('-target', args.clang_options): + src = args.input_files[0] + if os.path.splitext(src)[1] == '.bc': + ll = temporary_file( + os.path.splitext( + os.path.basename(src))[0], + '.ll', + args) + try_command(['llvm-dis', '-o', ll, src]) + src = ll + if os.path.splitext(src)[1] == '.ll': + with open(src, 'r') as f: + for line in f: + triple = re.findall('^target triple = "(.*)"', line) + if len(triple) > 0: + args.clang_options += (" -target %s" % triple[0]) + break - verifier_group.add_argument('--context-bound', metavar='K', default='1', type=int, - help='bound on the number of thread contexts in Corral [default: %(default)s]') - verifier_group.add_argument('--verifier-options', metavar='OPTIONS', default='', - help='additional verifier arguments (e.g., --verifier-options="/trackAllVars /staticInlining")') +def frontend(args): + """Generate the LLVM bitcode file.""" + bitcodes = [] + libs = set() + noreturning_frontend = False + + def add_libs(lang): + if lang in extra_libs(): + libs.add(extra_libs()[lang]) + + if args.language: + lang = languages()[args.language] + if lang in ['boogie', 'svcomp', 'json']: + noreturning_frontend = True + + add_libs(lang) + frontend = frontends()[lang] + for input_file in args.input_files: + bitcode = frontend(input_file, args) + if bitcode is not None: + bitcodes.append(bitcode) - verifier_group.add_argument('--time-limit', metavar='N', default='1200', type=int, - help='verifier time limit, in seconds [default: %(default)s]') + else: + for input_file in args.input_files: + lang = languages()[os.path.splitext(input_file)[1][1:]] + if lang in ['boogie', 'svcomp', 'json']: + noreturning_frontend = True - verifier_group.add_argument('--max-violations', metavar='N', default='1', type=int, - help='maximum reported assertion violations [default: %(default)s]') + add_libs(lang) + bitcode = frontends()[lang](input_file, args) + if bitcode is not None: + bitcodes.append(bitcode) - verifier_group.add_argument('--smackd', action="store_true", default=False, - help='generate JSON-format output for SMACKd') + if not noreturning_frontend: + return link_bc_files(bitcodes, libs, args) - verifier_group.add_argument('--svcomp-property', metavar='FILE', default=None, - type=str, help='load SVCOMP property to check from FILE') - verifier_group.add_argument('--modular', action="store_true", default=False, - help='enable contracts-based modular deductive verification (uses Boogie)') +def llvm_to_bpl(args): + """Translate the LLVM bitcode file to a Boogie source file.""" + + cmd = ['llvm2bpl', args.linked_bc_file, '-bpl', args.bpl_file] + cmd += ['-warn-type', args.warn] + cmd += ['-sea-dsa=ci'] + # This flag can lead to unsoundness in Rust regressions. + # cmd += ['-sea-dsa-type-aware'] + if sys.stdout.isatty(): + cmd += ['-colored-warnings'] + cmd += ['-source-loc-syms'] + for ep in args.entry_points: + cmd += ['-entry-points', ep] + if args.debug: + cmd += ['-debug'] + if args.debug_only: + cmd += ['-debug-only', args.debug_only] + if args.ll_file: + cmd += ['-ll', args.ll_file] + if "impls" in args.mem_mod: + cmd += ['-mem-mod-impls'] + if args.static_unroll: + cmd += ['-static-unroll'] + if args.integer_encoding == 'bit-vector': + cmd += ['-bit-precise'] + if args.integer_encoding == 'wrapped-integer': + cmd += ['-wrapped-integer-encoding'] + if args.timing_annotations: + cmd += ['-timing-annotations'] + if args.pointer_encoding == 'bit-vector': + cmd += ['-bit-precise-pointers'] + if args.no_byte_access_inference: + cmd += ['-no-byte-access-inference'] + if args.no_memory_splitting: + cmd += ['-no-memory-splitting'] + if ('memory-safety' in args.check or 'valid-deref' in args.check or + 'valid-free' in args.check or 'memleak' in args.check): + cmd += ['-memory-safety'] + if 'integer-overflow' in args.check: + cmd += ['-integer-overflow'] + if 'rust-panics' in args.check: + cmd += ['-rust-panics'] + if args.llvm_assumes: + cmd += ['-llvm-assumes=' + args.llvm_assumes] + if args.float: + cmd += ['-float'] + if args.modular: + cmd += ['-modular'] + try_command(cmd, console=True) + annotate_bpl(args) + memsafety_subproperty_selection(args) + transform_bpl(args) - verifier_group.add_argument('--replay', action="store_true", default=False, - help='enable reply of error trace with test harness.') - plugins_group = parser.add_argument_group('plugins') +def procedure_annotation(name, args): + if name in args.entry_points: + return "{:entrypoint}" + elif (args.modular and + re.match("|".join(inlined_procedures()).replace("$", r"\$"), name)): + return "{:inline 1}" + elif (not args.modular) and args.verifier == 'boogie': + return ("{:inline %s}" % args.unroll) + else: + return "" - plugins_group.add_argument('--transform-bpl', metavar='COMMAND', default=None, - type=str, help='transform generated Boogie code via COMMAND') - plugins_group.add_argument('--transform-out', metavar='COMMAND', default=None, - type=str, help='transform verifier output via COMMAND') +def annotate_bpl(args): + """Annotate the Boogie source file with additional metadata.""" + + proc_decl = re.compile(r'procedure\s+([^\s(]*)\s*\(') + + with open(args.bpl_file, 'r+') as f: + bpl = "// generated by SMACK version %s for %s\n" % ( + VERSION, args.verifier) + bpl += "// via %s\n\n" % " ".join(sys.argv) + bpl += proc_decl.sub( + lambda m: ("procedure %s %s(" % + (procedure_annotation(m.group(1), args), m.group(1))), + f.read()) + f.seek(0) + f.truncate() + f.write(bpl) + + +def memsafety_subproperty_selection(args): + selected_props = {} + if 'memory-safety' in args.check: + return + if 'valid-deref' in args.check: + selected_props.add('valid_deref') + if 'valid-free' in args.check: + selected_props.add('valid_free') + if 'memleak' in args.check: + selected_props.add('valid_memtrack') + + def replace_assertion(m): + if len(selected_props) > 0: + if m.group(2) and m.group(3) in selected_props: + attrib = m.group(2) + expr = m.group(4) + else: + attrib = '' + expr = 'true' + return m.group(1) + attrib + expr + ";" + else: + return m.group(0) + + with open(args.bpl_file, 'r+') as f: + lines = f.readlines() + f.seek(0) + f.truncate() + for line in lines: + line = re.sub( + r'^(\s*assert\s*)({:(.+)})?(.+);', + replace_assertion, + line) + f.write(line) - args = parser.parse_args() - if not args.bc_file: - args.bc_file = temporary_file('a', '.bc', args) +def transform_bpl(args): + if args.transform_bpl: + with open(args.bpl_file, 'r+') as bpl: + old = bpl.read() + bpl.seek(0) + bpl.truncate() + tx = subprocess.Popen( + shlex.split( + args.transform_bpl), + stdin=subprocess.PIPE, + stdout=bpl, + universal_newlines=True) + tx.communicate(input=old) - if not args.linked_bc_file: - args.linked_bc_file = temporary_file('b', '.bc', args) - if not args.bpl_file: - args.bpl_file = 'a.bpl' if args.no_verify else temporary_file('a', '.bpl', args) +def transform_out(args, old): + out = old + if args.transform_out: + tx = subprocess.Popen(shlex.split(args.transform_out), + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, universal_newlines=True) + out, err = tx.communicate(input=old) + return out - if args.only_check_valid_deref or args.only_check_valid_free or args.only_check_memleak: - args.memory_safety = True - if args.bit_precise_pointers: - args.bit_precise = True +def verification_result(verifier_output): + if re.search( + r'[1-9]\d* time out|Z3 ran out of resources|timed out|ERRORS_TIMEOUT', + verifier_output): + return 'timeout' + elif re.search((r'[1-9]\d* verified, 0 errors?|no bugs|' + r'NO_ERRORS_NO_TIMEOUT'), verifier_output): + return 'verified' + elif re.search((r'\d* verified, [1-9]\d* errors?|can fail|' + r'ERRORS_NO_TIMEOUT'), verifier_output): + if re.search( + r'ASSERTION FAILS assert {:valid_deref}', + verifier_output): + return 'invalid-deref' + elif re.search(r'ASSERTION FAILS assert {:valid_free}', + verifier_output): + return 'invalid-free' + elif re.search(r'ASSERTION FAILS assert {:valid_memtrack}', + verifier_output): + return 'invalid-memtrack' + elif re.search(r'ASSERTION FAILS assert {:overflow}', verifier_output): + return 'overflow' + elif re.search(r'ASSERTION FAILS assert {:rust_panic}', + verifier_output): + return 'rust-panic' + else: + listCall = re.findall(r'\(CALL .+\)', verifier_output) + if len(listCall) > 0 and re.search( + r'free_', listCall[len(listCall) - 1]): + return 'invalid-free' + else: + return 'error' + else: + return 'unknown' - # TODO are we (still) using this? - # with open(args.input_file, 'r') as f: - # for line in f.readlines(): - # m = re.match('.*SMACK-OPTIONS:[ ]+(.*)$', line) - # if m: - # return args = parser.parse_args(m.group(1).split() + sys.argv[1:]) - return args +def verify_bpl(args): + """Verify the Boogie source file with a back-end verifier.""" + + if args.verifier == 'svcomp': + verify_bpl_svcomp(args) + return + + elif args.verifier == 'boogie' or args.modular: + command = ["boogie"] + command += [args.bpl_file] + command += ["/nologo", "/doModSetAnalysis"] + command += ["/useArrayTheory"] + command += ["/timeLimit:%s" % args.time_limit] + command += ["/errorLimit:%s" % args.max_violations] + command += ["/proverOpt:O:smt.array.extensional=false"] + command += ["/proverOpt:O:smt.qi.eager_threshold=100"] + if not args.modular: + command += ["/loopUnroll:%d" % args.unroll] + if args.solver == 'cvc4': + command += ["/proverOpt:SOLVER=cvc4"] + elif args.solver == 'yices2': + command += ["/proverOpt:SOLVER=Yices2"] + + elif args.verifier == 'corral': + command = ["corral"] + command += [args.bpl_file] + command += ["/tryCTrace", "/noTraceOnDisk", "/printDataValues:1"] + command += ["/k:%d" % args.context_bound] + command += ["/useProverEvaluate"] + command += ["/timeLimit:%s" % args.time_limit] + command += ["/cex:%s" % args.max_violations] + command += ["/maxStaticLoopBound:%d" % args.loop_limit] + command += ["/recursionBound:%d" % args.unroll] + command += ["/bopt:proverOpt:O:smt.qi.eager_threshold=100"] + if args.solver == 'cvc4': + command += ["/bopt:proverOpt:SOLVER=cvc4"] + elif args.solver == 'yices2': + command += ["/bopt:proverOpt:SOLVER=Yices2"] + + elif args.verifier == 'symbooglix': + command = ['symbooglix'] + command += [args.bpl_file] + command += ["--file-logging=0"] + command += ["--entry-points=%s" % ",".join(args.entry_points)] + command += ["--timeout=%d" % args.time_limit] + command += ["--max-loop-depth=%d" % args.unroll] + + if args.verifier_options: + command += args.verifier_options.split() + + verifier_output = try_command(command, timeout=args.time_limit) + verifier_output = transform_out(args, verifier_output) + result = verification_result(verifier_output) + + if args.smackd: + print(smackdOutput(verifier_output)) + else: + if (result == 'error' or result == 'invalid-deref' or + result == 'invalid-free' or result == 'invalid-memtrack' or + result == 'overflow' or result == 'rust-panic'): + error = error_trace(verifier_output, args) -def target_selection(args): - """Determine the target architecture based on flags and source files.""" - # TODO more possible clang flags that determine the target? - if not re.search('-target', args.clang_options): - src = args.input_files[0] - if os.path.splitext(src)[1] == '.bc': - ll = temporary_file(os.path.splitext(os.path.basename(src))[0], '.ll', args) - try_command(['llvm-dis', '-o', ll, src]) - src = ll - if os.path.splitext(src)[1] == '.ll': - with open(src, 'r') as f: - for line in f: - triple = re.findall('^target triple = "(.*)"', line) - if len(triple) > 0: - args.clang_options += (" -target %s" % triple[0]) - break + if args.error_file: + with open(args.error_file, 'w') as f: + f.write(error) -def frontend(args): - """Generate the LLVM bitcode file.""" - bitcodes = [] - libs = set() - noreturning_frontend = False - - def add_libs(lang): - if lang in extra_libs(): - libs.add(extra_libs()[lang]) - - if args.language: - lang = languages()[args.language] - if lang in ['boogie', 'svcomp', 'json']: - noreturning_frontend = True - - add_libs(lang) - frontend = frontends()[lang] - for input_file in args.input_files: - bitcode = frontend(input_file,args) - if bitcode is not None: - bitcodes.append(bitcode) - - else: - for input_file in args.input_files: - lang = languages()[os.path.splitext(input_file)[1][1:]] - if lang in ['boogie', 'svcomp', 'json']: - noreturning_frontend = True - - add_libs(lang) - bitcode = frontends()[lang](input_file,args) - if bitcode is not None: - bitcodes.append(bitcode) - - if not noreturning_frontend: - return link_bc_files(bitcodes,libs,args) + if not args.quiet: + print(error) -def llvm_to_bpl(args): - """Translate the LLVM bitcode file to a Boogie source file.""" - - cmd = ['llvm2bpl', args.linked_bc_file, '-bpl', args.bpl_file] - cmd += ['-warn-type', args.warn] - cmd += ['-sea-dsa=ci'] - # This flag can lead to unsoundness in Rust regressions. - #cmd += ['-sea-dsa-type-aware'] - if sys.stdout.isatty(): cmd += ['-colored-warnings'] - cmd += ['-source-loc-syms'] - for ep in args.entry_points: - cmd += ['-entry-points', ep] - if args.debug: cmd += ['-debug'] - if args.debug_only: cmd += ['-debug-only', args.debug_only] - if args.ll_file: cmd += ['-ll', args.ll_file] - if "impls" in args.mem_mod:cmd += ['-mem-mod-impls'] - if args.static_unroll: cmd += ['-static-unroll'] - if args.bit_precise: cmd += ['-bit-precise'] - if args.timing_annotations: cmd += ['-timing-annotations'] - if args.bit_precise_pointers: cmd += ['-bit-precise-pointers'] - if args.no_byte_access_inference: cmd += ['-no-byte-access-inference'] - if args.no_memory_splitting: cmd += ['-no-memory-splitting'] - if args.memory_safety: cmd += ['-memory-safety'] - if args.integer_overflow: cmd += ['-integer-overflow'] - if args.llvm_assumes: cmd += ['-llvm-assumes=' + args.llvm_assumes] - if args.float: cmd += ['-float'] - if args.modular: cmd += ['-modular'] - try_command(cmd, console=True) - annotate_bpl(args) - property_selection(args) - transform_bpl(args) + if args.replay: + replay_error_trace(verifier_output, args) + print(results(args)[result][0]) + sys.exit(results(args)[result][1]) -def procedure_annotation(name, args): - if name in args.entry_points: - return "{:entrypoint}" - elif args.modular and re.match("|".join(inlined_procedures()).replace("$","\$"), name): - return "{:inline 1}" - elif (not args.modular) and args.verifier == 'boogie': - return ("{:inline %s}" % args.unroll) - else: - return "" -def annotate_bpl(args): - """Annotate the Boogie source file with additional metadata.""" - - proc_decl = re.compile('procedure\s+([^\s(]*)\s*\(') - - with open(args.bpl_file, 'r+') as f: - bpl = "// generated by SMACK version %s for %s\n" % (VERSION, args.verifier) - bpl += "// via %s\n\n" % " ".join(sys.argv) - bpl += proc_decl.sub(lambda m: ("procedure %s %s(" % (procedure_annotation(m.group(1), args), m.group(1))), f.read()) - f.seek(0) - f.truncate() - f.write(bpl) - -def property_selection(args): - selected_props = [] - if args.only_check_valid_deref: - selected_props.append('valid_deref') - elif args.only_check_valid_free: - selected_props.append('valid_free') - elif args.only_check_memleak: - selected_props.append('valid_memtrack') - - def replace_assertion(m): - if len(selected_props) > 0: - if m.group(2) and m.group(3) in selected_props: - attrib = m.group(2) - expr = m.group(4) - else: - attrib = '' - expr = 'true' - return m.group(1) + attrib + expr + ";" +def error_step(step): + FILENAME = r'[\w#$~%.\/-]*' + step = re.match(r"(\s*)(%s)\((\d+),\d+\): (.*)" % FILENAME, step) + if step: + if re.match('.*[.]bpl$', step.group(2)): + line_no = int(step.group(3)) + message = step.group(4) + if re.match(r'.*\$bb\d+.*', message): + message = "" + with open(step.group(2)) as f: + for line in f.read().splitlines(True)[line_no:line_no + 10]: + src = re.match( + r".*{:sourceloc \"(%s)\", (\d+), (\d+)}" % + FILENAME, line) + if src: + return "%s%s(%s,%s): %s" % (step.group(1), src.group( + 1), src.group(2), src.group(3), message) + else: + return corral_error_step(step.group(0)) else: - return m.group(0) + return None - with open(args.bpl_file, 'r+') as f: - lines = f.readlines() - f.seek(0) - f.truncate() - for line in lines: - line = re.sub(r'^(\s*assert\s*)({:(.+)})?(.+);', replace_assertion, line) - f.write(line) -def transform_bpl(args): - if args.transform_bpl: - with open(args.bpl_file, 'r+') as bpl: - old = bpl.read() - bpl.seek(0) - bpl.truncate() - tx = subprocess.Popen(shlex.split(args.transform_bpl), - stdin=subprocess.PIPE, stdout=bpl, universal_newlines=True) - tx.communicate(input = old) +def reformat_assignment(line): + def repl(m): + val = m.group(1) + if 'bv' in val: + return m.group(2) + 'UL' + else: + sig_size = int(m.group(7)) + exp_size = int(m.group(8)) + # assume we can only handle double + if sig_size > 53 or exp_size > 11: + return m.group() + + sign_val = -1 if m.group(3) != '' else 1 + sig_val = m.group(4) + exp_sign_val = -1 if m.group(5) != '' else 1 + # note that the exponent base is 16 + exp_val = 2**(4 * exp_sign_val * int(m.group(6))) + return str(sign_val * float.fromhex(sig_val) * exp_val) + + # Boogie FP const grammar: (-)0x[sig]e[exp]f[sigSize]e[expSize], where + # sig = hexdigit {hexdigit} '.' hexdigit {hexdigit} + # exp = digit {digit} + # sigSize = digit {digit} + # expSize = digit {digit} + return re.sub( + (r'((\d+)bv\d+|(-?)0x([0-9a-fA-F]+\.[0-9a-fA-F]+)e(-?)' + r'(\d+)f(\d+)e(\d+))'), + repl, + line.strip()) + + +def demangle(func): + def demangle_with(func, tool): + if shutil.which(tool): + p = subprocess.Popen( + tool, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, _ = p.communicate(input=func.encode()) + return out.decode() + return func + return functools.reduce(demangle_with, ['cxxfilt', 'rustfilt'], func) -def transform_out(args, old): - out = old - if args.transform_out: - tx = subprocess.Popen(shlex.split(args.transform_out), - stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, universal_newlines=True) - out, err = tx.communicate(input = old) - return out -def verification_result(verifier_output): - if re.search(r'[1-9]\d* time out|Z3 ran out of resources|timed out|ERRORS_TIMEOUT', verifier_output): - return 'timeout' - elif re.search(r'[1-9]\d* verified, 0 errors?|no bugs|NO_ERRORS_NO_TIMEOUT', verifier_output): - return 'verified' - elif re.search(r'\d* verified, [1-9]\d* errors?|can fail|ERRORS_NO_TIMEOUT', verifier_output): - if re.search(r'ASSERTION FAILS assert {:valid_deref}', verifier_output): - return 'invalid-deref' - elif re.search(r'ASSERTION FAILS assert {:valid_free}', verifier_output): - return 'invalid-free' - elif re.search(r'ASSERTION FAILS assert {:valid_memtrack}', verifier_output): - return 'invalid-memtrack' - elif re.search(r'ASSERTION FAILS assert {:overflow}', verifier_output): - return 'overflow' +def transform(info): + info = info.strip() + if info.startswith('CALL') or info.startswith('RETURN from'): + tokens = info.split() + tokens[-1] = demangle(tokens[-1]) + return ' '.join(tokens) + elif '=' in info: + tokens = info.split('=') + lhs = tokens[0].strip() + rhs = tokens[1].strip() + return demangle(lhs) + ' = ' + reformat_assignment(rhs) else: - listCall = re.findall(r'\(CALL .+\)', verifier_output) - if len(listCall) > 0 and re.search(r'free_', listCall[len(listCall)-1]): - return 'invalid-free' - else: - return 'error' - else: - return 'unknown' + return info -def verify_bpl(args): - """Verify the Boogie source file with a back-end verifier.""" - - if args.verifier == 'svcomp': - verify_bpl_svcomp(args) - return - - elif args.verifier == 'boogie' or args.modular: - command = ["boogie"] - command += [args.bpl_file] - command += ["/nologo", "/noinfer", "/doModSetAnalysis"] - command += ["/timeLimit:%s" % args.time_limit] - command += ["/errorLimit:%s" % args.max_violations] - if not args.modular: - command += ["/loopUnroll:%d" % args.unroll] - if args.solver == 'cvc4': - command += ["/proverOpt:SOLVER=cvc4"] - - elif args.verifier == 'corral': - command = ["corral"] - command += [args.bpl_file] - command += ["/tryCTrace", "/noTraceOnDisk", "/printDataValues:1"] - command += ["/k:%d" % args.context_bound] - command += ["/useProverEvaluate"] - command += ["/timeLimit:%s" % args.time_limit] - command += ["/cex:%s" % args.max_violations] - command += ["/maxStaticLoopBound:%d" % args.loop_limit] - command += ["/recursionBound:%d" % args.unroll] - if args.solver == 'cvc4': - command += ["/bopt:proverOpt:SOLVER=cvc4"] - - elif args.verifier == 'symbooglix': - command = ['symbooglix'] - command += [args.bpl_file] - command += ["--file-logging=0"] - command += ["--entry-points=%s" % ",".join(args.entry_points)] - command += ["--timeout=%d" % args.time_limit] - command += ["--max-loop-depth=%d" % args.unroll] - - if (args.bit_precise or args.float) and args.verifier != 'symbooglix': - x = "bopt:" if args.verifier != 'boogie' else "" - command += ["/%sproverOpt:OPTIMIZE_FOR_BV=true" % x] - command += ["/%sboolControlVC" % x] - - if args.verifier_options: - command += args.verifier_options.split() - - verifier_output = try_command(command, timeout=args.time_limit) - verifier_output = transform_out(args, verifier_output) - result = verification_result(verifier_output) - - if args.smackd: - print(smackdOutput(verifier_output)) - - elif result == 'verified': - print(results(args)[result]) - - else: - if result == 'error' or result == 'invalid-deref' or result == 'invalid-free' or result == 'invalid-memtrack' or result == 'overflow': - error = error_trace(verifier_output, args) - - if args.error_file: - with open(args.error_file, 'w') as f: - f.write(error) - - if not args.quiet: - print(error) - - if args.replay: - replay_error_trace(verifier_output, args) - - sys.exit(results(args)[result]) -def error_step(step): - FILENAME = '[\w#$~%.\/-]*' - step = re.match("(\s*)(%s)\((\d+),\d+\): (.*)" % FILENAME, step) - if step: - if re.match('.*[.]bpl$', step.group(2)): - line_no = int(step.group(3)) - message = step.group(4) - if re.match('.*\$bb\d+.*', message): - message = "" - with open(step.group(2)) as f: - for line in f.read().splitlines(True)[line_no:line_no+10]: - src = re.match(".*{:sourceloc \"(%s)\", (\d+), (\d+)}" % FILENAME, line) - if src: - return "%s%s(%s,%s): %s" % (step.group(1), src.group(1), src.group(2), src.group(3), message) - else: - return corral_error_step(step.group(0)) - else: - return None - -def reformat_assignment(line): - def repl(m): - val = m.group(1) - if 'bv' in val: - return m.group(2)+'UL' +def corral_error_step(step): + m = re.match(r'([^\s]*)\s+Trace:\s+(Thread=\d+)\s+\((.*)[\)|;]', step) + if m: + path = m.group(1) + tid = m.group(2) + info = ','.join(map(transform, + [x for x in m.group(3).split(',') if not + re.search( + (r'((CALL|RETURN from)\s+(\$|__SMACK))|' + r'Done|ASSERTION'), x)])) + return '{0}\t{1} {2}'.format(path, tid, info) else: - sig_size = int(m.group(7)) - exp_size = int(m.group(8)) - # assume we can only handle double - if sig_size > 53 or exp_size > 11: - return m.group() - - sign_val = -1 if m.group(3) != '' else 1 - sig_val = m.group(4) - exp_sign_val = -1 if m.group(5) != '' else 1 - # note that the exponent base is 16 - exp_val = 2**(4*exp_sign_val*int(m.group(6))) - return str(sign_val*float.fromhex(sig_val)*exp_val) - - # Boogie FP const grammar: (-)0x[sig]e[exp]f[sigSize]e[expSize], where - # sig = hexdigit {hexdigit} '.' hexdigit {hexdigit} - # exp = digit {digit} - # sigSize = digit {digit} - # expSize = digit {digit} - return re.sub('((\d+)bv\d+|(-?)0x([0-9a-fA-F]+\.[0-9a-fA-F]+)e(-?)(\d+)f(\d+)e(\d+))', repl, line.strip()) - -def transform(info): - return ','.join(map(reformat_assignment, [x for x in info.split(',') if not re.search('((CALL|RETURN from)\s+(\$|__SMACK))|Done|ASSERTION', x)])) + return step -def corral_error_step(step): - m = re.match('([^\s]*)\s+Trace:\s+(Thread=\d+)\s+\((.*)[\)|;]', step) - if m: - path = m.group(1) - tid = m.group(2) - info = transform(m.group(3)) - return '{0}\t{1} {2}'.format(path,tid,info) - else: - return step def error_trace(verifier_output, args): - trace = "" - for line in verifier_output.splitlines(True): - step = error_step(line) - if step: - m = re.match('(.*): [Ee]rror [A-Z0-9]+: (.*)', step) - if m: - trace += "%s: %s\nExecution trace:\n" % (m.group(1), m.group(2)) - else: - trace += ('' if step[0] == ' ' else ' ') + step + "\n" + trace = "" + for line in verifier_output.splitlines(True): + step = error_step(line) + if step: + m = re.match('(.*): [Ee]rror [A-Z0-9]+: (.*)', step) + if m: + trace += "%s: %s\nExecution trace:\n" % ( + m.group(1), m.group(2)) + else: + trace += ('' if step[0] == ' ' else ' ') + step + "\n" + + return trace - return trace def smackdOutput(corralOutput): - FILENAME = '[\w#$~%.\/-]+' - traceP = re.compile('(' + FILENAME + ')\((\d+),(\d+)\): Trace: Thread=(\d+) (\((.*)[\);])?$') - errorP = re.compile('(' + FILENAME + ')\((\d+),(\d+)\): (error .*)$') - - passedMatch = re.search('Program has no bugs', corralOutput) - if passedMatch: - json_data = { - 'verifier': 'corral', - 'passed?': True - } - - else: - traces = [] - filename = '' - lineno = 0 - colno = 0 - threadid = 0 - desc = '' - for traceLine in corralOutput.splitlines(True): - traceMatch = traceP.match(traceLine) - if traceMatch: - filename = str(traceMatch.group(1)) - lineno = int(traceMatch.group(2)) - colno = int(traceMatch.group(3)) - threadid = int(traceMatch.group(4)) - desc = str(traceMatch.group(6)) - for e in desc.split(','): - e = e.strip() - assm = re.sub(r'=(\s*\d+)bv\d+', r'=\1', e) if '=' in e else '' - trace = { 'threadid': threadid, - 'file': filename, - 'line': lineno, - 'column': colno, - 'description': e, - 'assumption': assm } - traces.append(trace) - else: - errorMatch = errorP.match(traceLine) - if errorMatch: - filename = str(errorMatch.group(1)) - lineno = int(errorMatch.group(2)) - colno = int(errorMatch.group(3)) - desc = str(errorMatch.group(4)) - - failsAt = { 'file': filename, 'line': lineno, 'column': colno, 'description': desc } - - json_data = { - 'verifier': 'corral', - 'passed?': False, - 'failsAt': failsAt, - 'threadCount': 1, - 'traces': traces - } - json_string = json.dumps(json_data) - return json_string + FILENAME = r'[\w#$~%.\/-]+' + traceP = re.compile( + ('(' + + FILENAME + + r')\((\d+),(\d+)\): Trace: Thread=(\d+)(\((.*)[\);])?$')) + errorP = re.compile('(' + FILENAME + r')\((\d+),(\d+)\): (error .*)$') + + passedMatch = re.search('Program has no bugs', corralOutput) + if passedMatch: + json_data = { + 'verifier': 'corral', + 'passed?': True + } + + else: + traces = [] + filename = '' + lineno = 0 + colno = 0 + threadid = 0 + desc = '' + for traceLine in corralOutput.splitlines(True): + traceMatch = traceP.match(traceLine) + if traceMatch: + filename = str(traceMatch.group(1)) + lineno = int(traceMatch.group(2)) + colno = int(traceMatch.group(3)) + threadid = int(traceMatch.group(4)) + desc = str(traceMatch.group(6)) + for e in desc.split(','): + e = e.strip() + assm = re.sub( + r'=(\s*\d+)bv\d+', + r'=\1', + e) if '=' in e else '' + trace = {'threadid': threadid, + 'file': filename, + 'line': lineno, + 'column': colno, + 'description': e, + 'assumption': assm} + traces.append(trace) + else: + errorMatch = errorP.match(traceLine) + if errorMatch: + filename = str(errorMatch.group(1)) + lineno = int(errorMatch.group(2)) + colno = int(errorMatch.group(3)) + desc = str(errorMatch.group(4)) + + failsAt = { + 'file': filename, + 'line': lineno, + 'column': colno, + 'description': desc} + + json_data = { + 'verifier': 'corral', + 'passed?': False, + 'failsAt': failsAt, + 'threadCount': 1, + 'traces': traces + } + json_string = json.dumps(json_data) + return json_string + def clean_up_upon_sigterm(main): - def handler(signum, frame): - remove_temp_files() - sys.exit(0) - signal.signal(signal.SIGTERM, handler) - return main + def handler(signum, frame): + remove_temp_files() + sys.exit(0) + signal.signal(signal.SIGTERM, handler) + return main + @clean_up_upon_sigterm def main(): - try: - global args - args = arguments() + try: + global args + args = arguments() - target_selection(args) + target_selection(args) - if not args.quiet: - print("SMACK program verifier version %s" % VERSION) + if not args.quiet: + print("SMACK program verifier version %s" % VERSION) - frontend(args) + frontend(args) - if args.no_verify: - if not args.quiet: - print("SMACK generated %s" % args.bpl_file) - else: - verify_bpl(args) + if args.no_verify: + if not args.quiet: + print("SMACK generated %s" % args.bpl_file) + else: + verify_bpl(args) - except KeyboardInterrupt: - sys.exit("SMACK aborted by keyboard interrupt.") + except KeyboardInterrupt: + sys.exit("SMACK aborted by keyboard interrupt.") - finally: - remove_temp_files() + finally: + remove_temp_files() diff --git a/share/smack/utils.py b/share/smack/utils.py index 908b005ac..2ba8e7978 100644 --- a/share/smack/utils.py +++ b/share/smack/utils.py @@ -8,75 +8,86 @@ temporary_files = [] + def temporary_file(prefix, extension, args): - f, name = tempfile.mkstemp(extension, prefix + '-', os.getcwd(), True) - os.close(f) - if not args.debug: - temporary_files.append(name) - return name + f, name = tempfile.mkstemp(extension, prefix + '-', os.getcwd(), True) + os.close(f) + if not args.debug: + temporary_files.append(name) + return name + def remove_temp_files(): - for f in temporary_files: - if os.path.isfile(f): - os.unlink(f) + for f in temporary_files: + if os.path.isfile(f): + os.unlink(f) + def timeout_killer(proc, timed_out): - if not timed_out[0]: - timed_out[0] = True - os.killpg(os.getpgid(proc.pid), signal.SIGKILL) + if not timed_out[0]: + timed_out[0] = True + os.killpg(os.getpgid(proc.pid), signal.SIGKILL) + def try_command(cmd, cwd=None, console=False, timeout=None): - args = top.args - console = (console or args.verbose or args.debug) and not args.quiet - filelog = args.debug - output = '' - proc = None - timer = None - try: - if args.debug: - print("Running %s" % " ".join(cmd)) - - proc = subprocess.Popen(cmd, cwd=cwd, preexec_fn=os.setsid, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - - if timeout: - timed_out = [False] - timer = Timer(timeout, timeout_killer, [proc, timed_out]) - timer.start() - - if console: - while True: - line = proc.stdout.readline() - if line: - output += line - print(line, end=' ') - elif proc.poll() is not None: - break - proc.wait - else: - output = proc.communicate()[0] - - if timeout: - timer.cancel() - - rc = proc.returncode + args = top.args + console = (console or args.verbose or args.debug) and not args.quiet + filelog = args.debug + output = '' proc = None - if timeout and timed_out[0]: - return output + ("\n%s timed out." % cmd[0]) - elif rc == -signal.SIGSEGV: - raise Exception("segmentation fault") - elif rc and args.verifier != 'symbooglix': - raise Exception(output) - else: - return output - - except (RuntimeError, OSError) as err: - print(output, file=sys.stderr) - sys.exit("Error invoking command:\n%s\n%s" % (" ".join(cmd), err)) - - finally: - if timeout and timer: timer.cancel() - if proc: os.killpg(os.getpgid(proc.pid), signal.SIGKILL) - if filelog: - with open(temporary_file(cmd[0], '.log', args), 'w') as f: - f.write(output) + timer = None + try: + if args.debug: + print("Running %s" % " ".join(cmd)) + + proc = subprocess.Popen( + cmd, + cwd=cwd, + preexec_fn=os.setsid, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + + if timeout: + timed_out = [False] + timer = Timer(timeout, timeout_killer, [proc, timed_out]) + timer.start() + + if console: + while True: + line = proc.stdout.readline() + if line: + output += line + print(line, end=' ') + elif proc.poll() is not None: + break + proc.wait + else: + output = proc.communicate()[0] + + if timeout: + timer.cancel() + + rc = proc.returncode + proc = None + if timeout and timed_out[0]: + return output + ("\n%s timed out." % cmd[0]) + elif rc == -signal.SIGSEGV: + raise Exception("segmentation fault") + elif rc and args.verifier != 'symbooglix': + raise Exception(output) + else: + return output + + except (RuntimeError, OSError) as err: + print(output, file=sys.stderr) + sys.exit("Error invoking command:\n%s\n%s" % (" ".join(cmd), err)) + + finally: + if timeout and timer: + timer.cancel() + if proc: + os.killpg(os.getpgid(proc.pid), signal.SIGKILL) + if filelog: + with open(temporary_file(cmd[0], '.log', args), 'w') as f: + f.write(output) diff --git a/test/c/basic/config.yml b/test/c/basic/config.yml deleted file mode 100644 index 65fa4127a..000000000 --- a/test/c/basic/config.yml +++ /dev/null @@ -1 +0,0 @@ -skip : false diff --git a/test/c/basic/gcd_1_true.c b/test/c/basic/gcd_1_true.c deleted file mode 100644 index cbc929b0e..000000000 --- a/test/c/basic/gcd_1_true.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "smack.h" - -// @expect verified - -signed int gcd_test(signed int a, signed int b) { - signed int t; - - if (a < 0) - a = -a; - if (b < 0) - b = -b; - - while (b != 0) { - t = b; - b = a % b; - a = t; - } - return t; -} - -int main() { - // signed int x = __VERIFIER_nondet_int(); - // signed int y = __VERIFIER_nondet_int(); - signed int x = 12; - signed int y = 4; - signed int g; - - if (y > 0 && x % y == 0) { - g = gcd_test(x, y); - assert(g == y); - } - - return 0; -} diff --git a/test/c/basic/gcd_fail.c b/test/c/basic/gcd_fail.c new file mode 100644 index 000000000..0f15144a3 --- /dev/null +++ b/test/c/basic/gcd_fail.c @@ -0,0 +1,33 @@ +// This test shows why we need parallel assignment when translating Phi nodes +#include "smack.h" + +// @expect error + +int gcd_test(int a, int b) { + int t; + + if (a < 0) + a = -a; + if (b < 0) + b = -b; + + while (b != 1) { + t = b; + b = a % b; + a = t; + } + return a; +} + +int main(void) { + int x = __VERIFIER_nondet_int(); + int y = __VERIFIER_nondet_int(); + int g; + + if (y > 0 && x > 0 && x % y == 0) { + g = gcd_test(x, y); + assert(g == y); + } + + return 0; +} diff --git a/test/c/basic/sext.c b/test/c/basic/sext.c new file mode 100644 index 000000000..7da9d3b3f --- /dev/null +++ b/test/c/basic/sext.c @@ -0,0 +1,15 @@ +#include "smack.h" +#include + +// @expect verified +// @flag --integer-encoding=wrapped-integer + +int main(void) { + int xs = INT_MAX; + long xl = xs; + assert(xl == INT_MAX); + xs = -1; + xl = xs; + assert(xl == -1); + return 0; +} diff --git a/test/c/basic/sext_fail.c b/test/c/basic/sext_fail.c new file mode 100644 index 000000000..de1f79f8d --- /dev/null +++ b/test/c/basic/sext_fail.c @@ -0,0 +1,15 @@ +#include "smack.h" +#include + +// @expect error +// @flag --integer-encoding=wrapped-integer + +int main(void) { + int xs = INT_MAX; + long xl = xs; + assert(xl == INT_MAX); + xs = -1; + xl = xs; + assert(xl != -1); + return 0; +} diff --git a/test/c/basic/trunc.c b/test/c/basic/trunc.c new file mode 100644 index 000000000..fe625e15c --- /dev/null +++ b/test/c/basic/trunc.c @@ -0,0 +1,18 @@ +#include "smack.h" +#include + +// @expect verified +// @flag --integer-encoding=wrapped-integer + +int main(void) { + unsigned long xl = ULONG_MAX; + unsigned xs = xl; + assert(xs == UINT_MAX); + long yl = LONG_MAX; + int ys = yl; + assert(ys == -1); + xs = yl; + ys = xl; + assert(xs == ys); + return 0; +} diff --git a/test/c/basic/trunc_fail.c b/test/c/basic/trunc_fail.c new file mode 100644 index 000000000..8e48f3fe9 --- /dev/null +++ b/test/c/basic/trunc_fail.c @@ -0,0 +1,18 @@ +#include "smack.h" +#include + +// @expect error +// @flag --integer-encoding=wrapped-integer + +int main(void) { + unsigned long xl = ULONG_MAX; + unsigned xs = xl; + assert(xs == UINT_MAX); + long yl = LONG_MAX; + int ys = yl; + assert(ys == -1); + xs = yl; + ys = xl; + assert(xs != ys); + return 0; +} diff --git a/test/c/basic/unsigned_max.c b/test/c/basic/unsigned_max.c new file mode 100644 index 000000000..819b31cd3 --- /dev/null +++ b/test/c/basic/unsigned_max.c @@ -0,0 +1,14 @@ +#include "smack.h" +#include + +// @expect verified +// @flag --integer-encoding=wrapped-integer + +int main(void) { + unsigned x = __VERIFIER_nondet_unsigned(); + unsigned uint32_max = 0xffffffff; + + assume(x > uint32_max - 1); + assert(x > ((unsigned)-3)); + return 0; +} diff --git a/test/c/basic/unsigned_max_fail.c b/test/c/basic/unsigned_max_fail.c new file mode 100644 index 000000000..229f99efd --- /dev/null +++ b/test/c/basic/unsigned_max_fail.c @@ -0,0 +1,14 @@ +#include "smack.h" +#include + +// @expect error +// @flag --integer-encoding=wrapped-integer + +int main(void) { + unsigned x = __VERIFIER_nondet_unsigned(); + unsigned uint32_max = 0xffffffff; + + assume(x < uint32_max - 1); + assert(x >= ((unsigned)-2)); + return 0; +} diff --git a/test/c/basic/unsigned_underflow.c b/test/c/basic/unsigned_underflow.c new file mode 100644 index 000000000..d28b1cd83 --- /dev/null +++ b/test/c/basic/unsigned_underflow.c @@ -0,0 +1,11 @@ +#include + +// @expect verified +// @flag --integer-encoding=wrapped-integer + +int main() { + unsigned x = 2; + unsigned y = 3; + assert(x - y > 0); + return 0; +} diff --git a/test/c/basic/unsigned_underflow_fail.c b/test/c/basic/unsigned_underflow_fail.c new file mode 100644 index 000000000..360208df4 --- /dev/null +++ b/test/c/basic/unsigned_underflow_fail.c @@ -0,0 +1,11 @@ +#include + +// @expect error +// @flag --integer-encoding=wrapped-integer + +int main() { + unsigned x = 2; + unsigned y = 3; + assert(x - y < 1); + return 0; +} diff --git a/test/c/basic/zext.c b/test/c/basic/zext.c new file mode 100644 index 000000000..ee8045637 --- /dev/null +++ b/test/c/basic/zext.c @@ -0,0 +1,14 @@ +#include "smack.h" +#include + +// @expect verified +// @flag --integer-encoding=wrapped-integer + +int main(void) { + unsigned xs = UINT_MAX; + unsigned long xl = xs; + assert(xl == UINT_MAX); + long yl = xs; + assert(yl == xl); + return 0; +} diff --git a/test/c/basic/zext_fail.c b/test/c/basic/zext_fail.c new file mode 100644 index 000000000..c99980b55 --- /dev/null +++ b/test/c/basic/zext_fail.c @@ -0,0 +1,14 @@ +#include "smack.h" +#include + +// @expect error +// @flag --integer-encoding=wrapped-integer + +int main(void) { + unsigned xs = UINT_MAX; + unsigned long xl = xs; + assert(xl == UINT_MAX); + long yl = xs; + assert(yl != xl); + return 0; +} diff --git a/test/c/bits/config.yml b/test/c/bits/config.yml index 7b3fe7f15..8b26f93be 100644 --- a/test/c/bits/config.yml +++ b/test/c/bits/config.yml @@ -1,2 +1,2 @@ -flags: [--bit-precise] -time-limit: 120 +skip: ok +flags: [--integer-encoding=bit-vector] diff --git a/test/c/bits/left_shift_negative_fail.c b/test/c/bits/left_shift_negative_fail.c index 106963140..9095bcefb 100644 --- a/test/c/bits/left_shift_negative_fail.c +++ b/test/c/bits/left_shift_negative_fail.c @@ -1,7 +1,7 @@ #include "smack.h" // @expect error -// @flag --integer-overflow +// @flag --check=integer-overflow int main(void) { int x = __VERIFIER_nondet_int(); diff --git a/test/c/bits/left_shift_overflow.c b/test/c/bits/left_shift_overflow.c index 62042b03b..b8b9bbd77 100644 --- a/test/c/bits/left_shift_overflow.c +++ b/test/c/bits/left_shift_overflow.c @@ -1,7 +1,7 @@ #include "smack.h" // @expect verified -// @flag --integer-overflow +// @flag --check=integer-overflow int main(void) { int x = __VERIFIER_nondet_int(); diff --git a/test/c/bits/left_shift_overflow_fail.c b/test/c/bits/left_shift_overflow_fail.c index 656780a66..ee43fb381 100644 --- a/test/c/bits/left_shift_overflow_fail.c +++ b/test/c/bits/left_shift_overflow_fail.c @@ -1,7 +1,7 @@ #include "smack.h" // @expect error -// @flag --integer-overflow +// @flag --check=integer-overflow int main(void) { int x = __VERIFIER_nondet_int(); diff --git a/test/c/bits/left_shift_unsigned.c b/test/c/bits/left_shift_unsigned.c index 3a79adc16..a7ead0a89 100644 --- a/test/c/bits/left_shift_unsigned.c +++ b/test/c/bits/left_shift_unsigned.c @@ -1,7 +1,7 @@ #include "smack.h" // @expect verified -// @flag --integer-overflow +// @flag --check=integer-overflow int main(void) { unsigned int x = __VERIFIER_nondet_unsigned_int(); diff --git a/test/c/bits/left_shift_unsigned_fail.c b/test/c/bits/left_shift_unsigned_fail.c index abe7af378..54f6d78e1 100644 --- a/test/c/bits/left_shift_unsigned_fail.c +++ b/test/c/bits/left_shift_unsigned_fail.c @@ -1,7 +1,7 @@ #include "smack.h" // @expect error -// @flag --integer-overflow +// @flag --check=integer-overflow int main(void) { unsigned int x = __VERIFIER_nondet_unsigned_int(); diff --git a/test/c/bits/malloc_non_alias.c b/test/c/bits/malloc_non_alias.c index 9de350902..f14d2289c 100644 --- a/test/c/bits/malloc_non_alias.c +++ b/test/c/bits/malloc_non_alias.c @@ -1,7 +1,7 @@ #include "smack.h" #include -// @flag --bit-precise-pointers +// @flag --pointer-encoding=bit-vector // @expect verified int main(void) { diff --git a/test/c/bits/pack_struct.c b/test/c/bits/pack_struct.c index 5905eb805..6372d9c9a 100644 --- a/test/c/bits/pack_struct.c +++ b/test/c/bits/pack_struct.c @@ -10,7 +10,7 @@ struct S { // Clang packs each argument as a 64-bit integer, // which introduces false alarms without -// the `--bit-precise` flag +// the `--integer-encoding=bit-vector` flag bool eq(struct S p1, struct S p2) { return p1.y == p2.y; } int main(void) { diff --git a/test/c/bits/pack_struct_fail.c b/test/c/bits/pack_struct_fail.c index 0be2ac0aa..d52627740 100644 --- a/test/c/bits/pack_struct_fail.c +++ b/test/c/bits/pack_struct_fail.c @@ -10,7 +10,7 @@ struct S { // Clang packs each argument as a 64-bit integer, // which introduces false alarms without -// the `--bit-precise` flag +// the `--integer-encoding=bit-vector` flag bool eq(struct S p1, struct S p2) { return p1.y == p2.y; } int main(void) { diff --git a/test/c/bits/pointers.c b/test/c/bits/pointers.c new file mode 100644 index 000000000..eaaf4c2ee --- /dev/null +++ b/test/c/bits/pointers.c @@ -0,0 +1,14 @@ +#include "smack.h" +#include + +// @expect verified + +int main() { + int *a = (int *)malloc(sizeof(int)); + int **p = (int **)malloc(sizeof(int *)); + *a = 256; + *p = a; + assert(**p + 1 == 257); + free(a); + return 0; +} diff --git a/test/c/bits/pointers1.c b/test/c/bits/pointers1.c new file mode 100644 index 000000000..b90e08024 --- /dev/null +++ b/test/c/bits/pointers1.c @@ -0,0 +1,15 @@ +#include "smack.h" +#include + +// @expect verified +// @flag --pointer-encoding=bit-vector + +int main() { + int *a = (int *)malloc(sizeof(int)); + int **p = (int **)malloc(sizeof(int *)); + *a = 256; + *p = a; + assert(**p + 1 == 257); + free(a); + return 0; +} diff --git a/test/c/bits/pointers1_fail.c b/test/c/bits/pointers1_fail.c new file mode 100644 index 000000000..c103ca865 --- /dev/null +++ b/test/c/bits/pointers1_fail.c @@ -0,0 +1,15 @@ +#include "smack.h" +#include + +// @expect error +// @flag --pointer-encoding=bit-vector + +int main() { + int *a = (int *)malloc(sizeof(int)); + int **p = (int **)malloc(sizeof(int *)); + *a = 256; + *p = a; + assert(**p + 1 != 257); + free(a); + return 0; +} diff --git a/test/c/bits/pointers_fail.c b/test/c/bits/pointers_fail.c new file mode 100644 index 000000000..a6eea69b1 --- /dev/null +++ b/test/c/bits/pointers_fail.c @@ -0,0 +1,14 @@ +#include "smack.h" +#include + +// @expect error + +int main() { + int *a = (int *)malloc(sizeof(int)); + int **p = (int **)malloc(sizeof(int *)); + *a = 256; + *p = a; + assert(**p + 1 != 257); + free(a); + return 0; +} diff --git a/test/c/bits/test_memset.c b/test/c/bits/test_memset.c new file mode 100644 index 000000000..e5ff2b137 --- /dev/null +++ b/test/c/bits/test_memset.c @@ -0,0 +1,11 @@ +#include "smack.h" +#include + +// @expect verified + +int main(void) { + unsigned long long x = 0; + memset(&x, 1, sizeof(x)); + assert(x != 1); + return 0; +} diff --git a/test/c/bits/test_memset_fail.c b/test/c/bits/test_memset_fail.c new file mode 100644 index 000000000..6e1770f82 --- /dev/null +++ b/test/c/bits/test_memset_fail.c @@ -0,0 +1,11 @@ +#include "smack.h" +#include + +// @expect error + +int main(void) { + unsigned long long x = 0; + memset(&x, 1, sizeof(x)); + assert(x == 1); + return 0; +} diff --git a/test/c/data/struct_return_o1.c b/test/c/data/struct_return_o1.c new file mode 100644 index 000000000..cf7da2c09 --- /dev/null +++ b/test/c/data/struct_return_o1.c @@ -0,0 +1,25 @@ +#include "smack.h" + +// @expect verified +// @flag --clang-options=-O1 + +int *A; + +typedef struct { + int *a; + long b; +} S; + +S foo() { + S x = {A, 2L}; + assert(1); + return x; +} + +int main(void) { + int a = 1; + A = &a; + S y = foo(); + assert(*(y.a) == 1); + return 0; +} diff --git a/test/c/data/struct_return_o1_fail.c b/test/c/data/struct_return_o1_fail.c new file mode 100644 index 000000000..65c0e39e0 --- /dev/null +++ b/test/c/data/struct_return_o1_fail.c @@ -0,0 +1,25 @@ +#include "smack.h" + +// @expect error +// @flag --clang-options=-O1 + +int *A; + +typedef struct { + int *a; + long b; +} S; + +S foo() { + S x = {A, 2L}; + assert(1); + return x; +} + +int main(void) { + int a = 1; + A = &a; + S y = foo(); + assert(*(y.a) == 3); + return 0; +} diff --git a/test/c/failing/exit.c b/test/c/failing/exit.c index f0f67ab4f..69cb72b42 100644 --- a/test/c/failing/exit.c +++ b/test/c/failing/exit.c @@ -13,13 +13,11 @@ void *t1(void *arg) { // Should never run this line, since called pthread_exit pthread_exit((void *)6); + return 0; } -int main() { - +int main(void) { pthread_t t; - - int a; void *ret; pthread_create(&t, 0, t1, 0); diff --git a/test/c/float/bitcast.c b/test/c/float/bitcast.c index 4ec817503..6df09d69c 100644 --- a/test/c/float/bitcast.c +++ b/test/c/float/bitcast.c @@ -1,6 +1,6 @@ #include "smack.h" -// @flag --bit-precise --clang-options="-fno-strict-aliasing" +// @flag --integer-encoding=bit-vector --clang-options="-fno-strict-aliasing" // @expect verified int main(void) { diff --git a/test/c/float/bitcast_fail.c b/test/c/float/bitcast_fail.c index 700b18d44..6db6767f0 100644 --- a/test/c/float/bitcast_fail.c +++ b/test/c/float/bitcast_fail.c @@ -1,6 +1,6 @@ #include "smack.h" -// @flag --bit-precise --clang-options="-fno-strict-aliasing" +// @flag --integer-encoding=bit-vector --clang-options="-fno-strict-aliasing" // @expect error int main(void) { diff --git a/test/c/float/double_to_int.c b/test/c/float/double_to_int.c index fe516c6f2..961308fec 100644 --- a/test/c/float/double_to_int.c +++ b/test/c/float/double_to_int.c @@ -1,6 +1,6 @@ #include "smack.h" -// @flag --bit-precise +// @flag --integer-encoding=bit-vector // @expect verified int main(void) { diff --git a/test/c/float/double_to_int_fail.c b/test/c/float/double_to_int_fail.c index 5b51c613c..6103ed0a2 100644 --- a/test/c/float/double_to_int_fail.c +++ b/test/c/float/double_to_int_fail.c @@ -1,6 +1,6 @@ #include "smack.h" -// @flag --bit-precise +// @flag --integer-encoding=bit-vector // @expect error int main(void) { diff --git a/test/c/float/float_int_union.c b/test/c/float/float_int_union.c index 1a4764222..b9f802364 100644 --- a/test/c/float/float_int_union.c +++ b/test/c/float/float_int_union.c @@ -1,6 +1,6 @@ #include "smack.h" -// @flag --bit-precise +// @flag --integer-encoding=bit-vector // @expect verified union mix { diff --git a/test/c/float/float_int_union_fail.c b/test/c/float/float_int_union_fail.c index d8228a607..1bbd63de5 100644 --- a/test/c/float/float_int_union_fail.c +++ b/test/c/float/float_int_union_fail.c @@ -1,6 +1,6 @@ #include "smack.h" -// @flag --bit-precise +// @flag --integer-encoding=bit-vector // @expect error union mix { diff --git a/test/c/float/floor.c b/test/c/float/floor.c index 70cb3ab4e..17f6abefa 100644 --- a/test/c/float/floor.c +++ b/test/c/float/floor.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { assert(floor(2.7) == 2.0); diff --git a/test/c/float/floor_fail.c b/test/c/float/floor_fail.c index 833013849..d19a4ee40 100644 --- a/test/c/float/floor_fail.c +++ b/test/c/float/floor_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { assert(floor(2.7) == 2.0); diff --git a/test/c/float/half_intrinsics.c b/test/c/float/half_intrinsics.c index a789060c3..f023b570a 100644 --- a/test/c/float/half_intrinsics.c +++ b/test/c/float/half_intrinsics.c @@ -1,7 +1,7 @@ #include "math.h" #include "smack.h" -// @flag --bit-precise +// @flag --integer-encoding=bit-vector // @expect verified int main(void) { diff --git a/test/c/float/half_intrinsics_fail.c b/test/c/float/half_intrinsics_fail.c index 39460e032..257c5e6ce 100644 --- a/test/c/float/half_intrinsics_fail.c +++ b/test/c/float/half_intrinsics_fail.c @@ -1,7 +1,7 @@ #include "math.h" #include "smack.h" -// @flag --bit-precise +// @flag --integer-encoding=bit-vector // @expect error int main(void) { diff --git a/test/c/locks/config.yml b/test/c/locks/config.yml index ad5a2e1cd..1576aa77c 100644 --- a/test/c/locks/config.yml +++ b/test/c/locks/config.yml @@ -1,2 +1 @@ skip: ok -time-limit: 300 diff --git a/test/c/mathc/ceil.c b/test/c/mathc/ceil.c index 0908b2923..7934de5e7 100644 --- a/test/c/mathc/ceil.c +++ b/test/c/mathc/ceil.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/ceil_fail.c b/test/c/mathc/ceil_fail.c index 43c3dae98..2505a0c3a 100644 --- a/test/c/mathc/ceil_fail.c +++ b/test/c/mathc/ceil_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/ceilf.c b/test/c/mathc/ceilf.c index a541b6fcc..664f7af4c 100644 --- a/test/c/mathc/ceilf.c +++ b/test/c/mathc/ceilf.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/ceilf_fail.c b/test/c/mathc/ceilf_fail.c index 6bd4a3124..609370673 100644 --- a/test/c/mathc/ceilf_fail.c +++ b/test/c/mathc/ceilf_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/copysign.c b/test/c/mathc/copysign.c index 0a57c79e8..5d61e9cdb 100644 --- a/test/c/mathc/copysign.c +++ b/test/c/mathc/copysign.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/copysign_fail.c b/test/c/mathc/copysign_fail.c index f96995931..8bec02f5a 100644 --- a/test/c/mathc/copysign_fail.c +++ b/test/c/mathc/copysign_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/copysignf.c b/test/c/mathc/copysignf.c index be01c7f9a..2fbb2e77e 100644 --- a/test/c/mathc/copysignf.c +++ b/test/c/mathc/copysignf.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/copysignf_fail.c b/test/c/mathc/copysignf_fail.c index 87b445799..14e5ebe7c 100644 --- a/test/c/mathc/copysignf_fail.c +++ b/test/c/mathc/copysignf_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/fabs.c b/test/c/mathc/fabs.c index 2e990e771..796cee5b7 100644 --- a/test/c/mathc/fabs.c +++ b/test/c/mathc/fabs.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/fabs_fail.c b/test/c/mathc/fabs_fail.c index fa81d5da5..5b28242eb 100644 --- a/test/c/mathc/fabs_fail.c +++ b/test/c/mathc/fabs_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/fabsf.c b/test/c/mathc/fabsf.c index c32b22272..b78812e8e 100644 --- a/test/c/mathc/fabsf.c +++ b/test/c/mathc/fabsf.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/fabsf_fail.c b/test/c/mathc/fabsf_fail.c index 2af8561b8..39e34b8fa 100644 --- a/test/c/mathc/fabsf_fail.c +++ b/test/c/mathc/fabsf_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/floor.c b/test/c/mathc/floor.c index 8674e62af..d4a62f745 100644 --- a/test/c/mathc/floor.c +++ b/test/c/mathc/floor.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/floor_fail.c b/test/c/mathc/floor_fail.c index 71162268a..96265299a 100644 --- a/test/c/mathc/floor_fail.c +++ b/test/c/mathc/floor_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/floorf.c b/test/c/mathc/floorf.c index d66322946..6eeca9e0c 100644 --- a/test/c/mathc/floorf.c +++ b/test/c/mathc/floorf.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/floorf_fail.c b/test/c/mathc/floorf_fail.c index d0f615704..a665df901 100644 --- a/test/c/mathc/floorf_fail.c +++ b/test/c/mathc/floorf_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/fmod.c b/test/c/mathc/fmod.c index b83a47be6..c249f03d2 100644 --- a/test/c/mathc/fmod.c +++ b/test/c/mathc/fmod.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/fmod_fail.c b/test/c/mathc/fmod_fail.c index 2636cb9de..c81763568 100644 --- a/test/c/mathc/fmod_fail.c +++ b/test/c/mathc/fmod_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/fmodf.c b/test/c/mathc/fmodf.c index 600ca8836..399264ffc 100644 --- a/test/c/mathc/fmodf.c +++ b/test/c/mathc/fmodf.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/fmodf_fail.c b/test/c/mathc/fmodf_fail.c index da2c277c8..c6c17ac9a 100644 --- a/test/c/mathc/fmodf_fail.c +++ b/test/c/mathc/fmodf_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/issue_244.c b/test/c/mathc/issue_244.c index 37189651f..e8f9ea6ad 100644 --- a/test/c/mathc/issue_244.c +++ b/test/c/mathc/issue_244.c @@ -2,6 +2,6 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { assert(!__signbit(remainder(0.0, 1.0))); } diff --git a/test/c/mathc/issue_244_fail.c b/test/c/mathc/issue_244_fail.c index 16bd801ee..b55a8b911 100644 --- a/test/c/mathc/issue_244_fail.c +++ b/test/c/mathc/issue_244_fail.c @@ -2,6 +2,6 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { assert(__signbit(remainder(0.0, 1.0))); } diff --git a/test/c/mathc/lrint.c b/test/c/mathc/lrint.c index 29e331799..b2441e0e2 100644 --- a/test/c/mathc/lrint.c +++ b/test/c/mathc/lrint.c @@ -3,7 +3,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/lrint_fail.c b/test/c/mathc/lrint_fail.c index dcdc00742..5e1b1e2cf 100644 --- a/test/c/mathc/lrint_fail.c +++ b/test/c/mathc/lrint_fail.c @@ -3,7 +3,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/lrintf.c b/test/c/mathc/lrintf.c index 13d223d4d..9748e59b6 100644 --- a/test/c/mathc/lrintf.c +++ b/test/c/mathc/lrintf.c @@ -3,7 +3,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/lrintf_fail.c b/test/c/mathc/lrintf_fail.c index 58756948b..17afa2e21 100644 --- a/test/c/mathc/lrintf_fail.c +++ b/test/c/mathc/lrintf_fail.c @@ -3,7 +3,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/lround.c b/test/c/mathc/lround.c index f449d099c..5a2bd76d4 100644 --- a/test/c/mathc/lround.c +++ b/test/c/mathc/lround.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/lround_fail.c b/test/c/mathc/lround_fail.c index 7231a6391..802ba10ea 100644 --- a/test/c/mathc/lround_fail.c +++ b/test/c/mathc/lround_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/lroundf.c b/test/c/mathc/lroundf.c index 633104306..d34f1c978 100644 --- a/test/c/mathc/lroundf.c +++ b/test/c/mathc/lroundf.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/lroundf_fail.c b/test/c/mathc/lroundf_fail.c index 833100618..1550ab77d 100644 --- a/test/c/mathc/lroundf_fail.c +++ b/test/c/mathc/lroundf_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/modf.c b/test/c/mathc/modf.c index cb66a51f7..ee8052a4e 100644 --- a/test/c/mathc/modf.c +++ b/test/c/mathc/modf.c @@ -3,7 +3,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/modf_fail.c b/test/c/mathc/modf_fail.c index 70d1def75..56d0b0018 100644 --- a/test/c/mathc/modf_fail.c +++ b/test/c/mathc/modf_fail.c @@ -3,7 +3,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/modff.c b/test/c/mathc/modff.c index a4d802d74..a58a24e50 100644 --- a/test/c/mathc/modff.c +++ b/test/c/mathc/modff.c @@ -3,7 +3,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/modff_fail.c b/test/c/mathc/modff_fail.c index c1fd9fde0..4efda4774 100644 --- a/test/c/mathc/modff_fail.c +++ b/test/c/mathc/modff_fail.c @@ -3,7 +3,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/nearbyint.c b/test/c/mathc/nearbyint.c index 68fc5cea8..b220a43b3 100644 --- a/test/c/mathc/nearbyint.c +++ b/test/c/mathc/nearbyint.c @@ -3,7 +3,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/nearbyint_fail.c b/test/c/mathc/nearbyint_fail.c index 6ff2a84e5..692e34fb6 100644 --- a/test/c/mathc/nearbyint_fail.c +++ b/test/c/mathc/nearbyint_fail.c @@ -3,7 +3,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/nearbyintf.c b/test/c/mathc/nearbyintf.c index 93a1adfb4..c3b048f62 100644 --- a/test/c/mathc/nearbyintf.c +++ b/test/c/mathc/nearbyintf.c @@ -3,7 +3,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/nearbyintf_fail.c b/test/c/mathc/nearbyintf_fail.c index b832869e0..614a05473 100644 --- a/test/c/mathc/nearbyintf_fail.c +++ b/test/c/mathc/nearbyintf_fail.c @@ -3,7 +3,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/rint.c b/test/c/mathc/rint.c index c89e3e28c..d41859253 100644 --- a/test/c/mathc/rint.c +++ b/test/c/mathc/rint.c @@ -3,7 +3,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/rint_fail.c b/test/c/mathc/rint_fail.c index 0529afcbe..a3c84e380 100644 --- a/test/c/mathc/rint_fail.c +++ b/test/c/mathc/rint_fail.c @@ -3,7 +3,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/rintf.c b/test/c/mathc/rintf.c index 2ff91d707..d3cfdc7f8 100644 --- a/test/c/mathc/rintf.c +++ b/test/c/mathc/rintf.c @@ -3,7 +3,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/rintf_fail.c b/test/c/mathc/rintf_fail.c index 6c72fea46..23b41fa7a 100644 --- a/test/c/mathc/rintf_fail.c +++ b/test/c/mathc/rintf_fail.c @@ -3,7 +3,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/round.c b/test/c/mathc/round.c index e3d8195db..818a35138 100644 --- a/test/c/mathc/round.c +++ b/test/c/mathc/round.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/round_fail.c b/test/c/mathc/round_fail.c index fbf567af1..15356c30f 100644 --- a/test/c/mathc/round_fail.c +++ b/test/c/mathc/round_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/roundf.c b/test/c/mathc/roundf.c index 58c2e1b95..63374b1ba 100644 --- a/test/c/mathc/roundf.c +++ b/test/c/mathc/roundf.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/roundf_fail.c b/test/c/mathc/roundf_fail.c index cfa86d159..fe302f87a 100644 --- a/test/c/mathc/roundf_fail.c +++ b/test/c/mathc/roundf_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/sqrt.c b/test/c/mathc/sqrt.c index 89d3a81e8..b4a15ae53 100644 --- a/test/c/mathc/sqrt.c +++ b/test/c/mathc/sqrt.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/sqrt_fail.c b/test/c/mathc/sqrt_fail.c index 4cb46720b..c2edb4471 100644 --- a/test/c/mathc/sqrt_fail.c +++ b/test/c/mathc/sqrt_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/sqrtf.c b/test/c/mathc/sqrtf.c index 7b2f01d96..d474cb549 100644 --- a/test/c/mathc/sqrtf.c +++ b/test/c/mathc/sqrtf.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/sqrtf_fail.c b/test/c/mathc/sqrtf_fail.c index 17c6c7700..7a1c59f18 100644 --- a/test/c/mathc/sqrtf_fail.c +++ b/test/c/mathc/sqrtf_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/trunc.c b/test/c/mathc/trunc.c index 96e5ba89b..7353cd2e2 100644 --- a/test/c/mathc/trunc.c +++ b/test/c/mathc/trunc.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/trunc_fail.c b/test/c/mathc/trunc_fail.c index 8a9dfb241..6672d8ce5 100644 --- a/test/c/mathc/trunc_fail.c +++ b/test/c/mathc/trunc_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { double NaN = 0.0 / 0.0; diff --git a/test/c/mathc/truncf.c b/test/c/mathc/truncf.c index 83b02f8f1..d00f466a8 100644 --- a/test/c/mathc/truncf.c +++ b/test/c/mathc/truncf.c @@ -2,7 +2,7 @@ #include // @expect verified -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/mathc/truncf_fail.c b/test/c/mathc/truncf_fail.c index 692b11740..3df6543ce 100644 --- a/test/c/mathc/truncf_fail.c +++ b/test/c/mathc/truncf_fail.c @@ -2,7 +2,7 @@ #include // @expect error -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { float NaN = 0.0f / 0.0f; diff --git a/test/c/memory-safety/config.yml b/test/c/memory-safety/config.yml index 778f5ee6c..e6fb12189 100644 --- a/test/c/memory-safety/config.yml +++ b/test/c/memory-safety/config.yml @@ -1,2 +1,3 @@ -flags: [--memory-safety] -time-limit: 180 +skip: ok +time-limit: 240 +flags: [--check=memory-safety] diff --git a/test/c/ntdrivers-simplified/config.yml b/test/c/ntdrivers-simplified/config.yml index ad5a2e1cd..1576aa77c 100644 --- a/test/c/ntdrivers-simplified/config.yml +++ b/test/c/ntdrivers-simplified/config.yml @@ -1,2 +1 @@ skip: ok -time-limit: 300 diff --git a/test/c/ntdrivers/config.yml b/test/c/ntdrivers/config.yml index ea8ecbbdb..9c41a1b3c 100644 --- a/test/c/ntdrivers/config.yml +++ b/test/c/ntdrivers/config.yml @@ -1,3 +1,2 @@ skip: ok -time-limit: 1600 -memory-limit: 2000 +time-limit: 600 diff --git a/test/c/failing/parport_false.i.cil.c b/test/c/ntdrivers/parport_false.i.cil.c similarity index 99% rename from test/c/failing/parport_false.i.cil.c rename to test/c/ntdrivers/parport_false.i.cil.c index e6dda6b9f..8f6290522 100644 --- a/test/c/failing/parport_false.i.cil.c +++ b/test/c/ntdrivers/parport_false.i.cil.c @@ -2,7 +2,6 @@ // @expect error -// see: https://github.com/smackers/smack/issues/554 extern char __VERIFIER_nondet_char(void); extern int __VERIFIER_nondet_int(void); extern long __VERIFIER_nondet_long(void); diff --git a/test/c/failing/parport_true.i.cil.c b/test/c/ntdrivers/parport_true.i.cil.c similarity index 99% rename from test/c/failing/parport_true.i.cil.c rename to test/c/ntdrivers/parport_true.i.cil.c index 1606668e4..36921913e 100644 --- a/test/c/failing/parport_true.i.cil.c +++ b/test/c/ntdrivers/parport_true.i.cil.c @@ -2,7 +2,6 @@ // @expect verified -// see: https://github.com/smackers/smack/issues/554 extern char __VERIFIER_nondet_char(void); extern int __VERIFIER_nondet_int(void); extern long __VERIFIER_nondet_long(void); diff --git a/test/c/pthread/config.yml b/test/c/pthread/config.yml index 51222f295..43b6a2d7e 100644 --- a/test/c/pthread/config.yml +++ b/test/c/pthread/config.yml @@ -1,5 +1,4 @@ skip: ok verifiers: [corral] memory: [no-reuse-impls] -time-limit: 300 flags: [--pthread, --context-bound=2] diff --git a/test/c/pthread/join_null_retval.c b/test/c/pthread/join_null_retval.c index 950e76a52..2c6b01a75 100644 --- a/test/c/pthread/join_null_retval.c +++ b/test/c/pthread/join_null_retval.c @@ -3,7 +3,7 @@ #include // @expect verified -// @flag --memory-safety +// @flag --check=memory-safety void *t1(void *arg) { return NULL; } diff --git a/test/c/failing/join_return.c b/test/c/pthread/join_return.c similarity index 89% rename from test/c/failing/join_return.c rename to test/c/pthread/join_return.c index 9e811a092..ee71ea176 100644 --- a/test/c/failing/join_return.c +++ b/test/c/pthread/join_return.c @@ -8,7 +8,6 @@ // @expect verified -// see: https://github.com/smackers/smack/issues/555 int x = 1; typedef struct pair { @@ -30,7 +29,7 @@ int main(void) { pair *ret; pthread_create(&t, 0, t1, 0); - pthread_join(t, &ret); + pthread_join(t, (void **)&ret); assert(x == 2); assert(ret->x == 3); assert(ret->y == 4); diff --git a/test/c/failing/join_return_fail.c b/test/c/pthread/join_return_fail.c similarity index 95% rename from test/c/failing/join_return_fail.c rename to test/c/pthread/join_return_fail.c index 9ac55cb23..959005bf8 100644 --- a/test/c/failing/join_return_fail.c +++ b/test/c/pthread/join_return_fail.c @@ -29,7 +29,7 @@ int main(void) { pair *ret; pthread_create(&t, 0, t1, 0); - pthread_join(t, &ret); + pthread_join(t, (void **)&ret); assert(x == 2); assert(ret->x == 3); assert(ret->y == 4); diff --git a/test/c/pthread/join_self_fail.c b/test/c/pthread/join_self_fail.c index 19aec8201..299f2643b 100644 --- a/test/c/pthread/join_self_fail.c +++ b/test/c/pthread/join_self_fail.c @@ -4,35 +4,15 @@ // Tests deadlock detection when join on self // @expect error -//////////////////////////////////////////////////////////////// -// -// Declare alternate functions for pthread_join(), -// __call_wrapper(), and pthread_create(), to illustrate -// failure when `assume(ctid == *__newthread)` is not present -// in __call_wrapper(). -// -// Definitions are found in this file, below the benchmark -// -//////////////////////////////////////////////////////////////// -int pthread_join2(pthread_t __th, void **__thread_return); -void __call_wrapper2(pthread_t *__restrict __newthread, - void *(*__start_routine)(void *), void *__restrict __arg); -int pthread_create2(pthread_t *__restrict __newthread, - __const pthread_attr_t *__restrict __attr, - void *(*__start_routine)(void *), void *__restrict __arg); -//////////////////////////////////////////////////////////////// -// Begin benchmark: -//////////////////////////////////////////////////////////////// - pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; void *t1(void *arg) { pthread_t *selfptr = (pthread_t *)arg; pthread_t self = *selfptr; int ret; - int err = pthread_join2(self, (void *)&ret); + int err = pthread_join(self, (void *)&ret); // Should be an EDEADLK error - assert(err == 35); + assert(err != 35); pthread_exit((void *)1); return 0; } @@ -40,97 +20,6 @@ void *t1(void *arg) { int main(void) { pthread_t tid1 = __VERIFIER_nondet_int(); int ret; - pthread_create2(&tid1, 0, t1, &tid1); - return 0; -} - -//////////////////////////////////////////////////////////////// -// End benchmark -//////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////// -// -// Below are alternate definitions for pthread_join(), -// __call_wrapper(), and pthread_create(), to illlustrate -// failure when `assume(ctid == *__newthread)` is not present -// in __call_wrapper(). -// -//////////////////////////////////////////////////////////////// -int pthread_join2(pthread_t __th, void **__thread_return) { - pthread_t calling_tid = pthread_self(); - - // Print the tid of the thread being joined to SMACK traces - int joining_tid = __th; - - // Check for self-joining deadlock - if (calling_tid == __th) - return 35; // This is EDEADLK - - // When verifying join_self.c, this next `assert(0)` should never be - // reached if the pthread model is correct beacuse calling_tid should - // be equal to __th in this function, so the if branch should always - // be taken, returning 35. - // - // However, when `assume(ctid == *__newthread)` is not present in - // __call_wrapper(), then it is possible that parent thread hasn't set - // the child thread ID in the original pthread_t struct, so calling_tid - // won't match the passed in argument, __th, and so this assert(0) is - // reachable in this case (of course, this is all specifically for - // join_self()). - assert(0); - - // Wait for the thread to terminate - __SMACK_code("assume $pthreadStatus[@][0] == $pthread_stopped;", __th); - - // Get the thread's return value - void *tmp_thread_return_pointer = __VERIFIER_nondet_pointer(); - __SMACK_code("@ := $pthreadStatus[@][1];", tmp_thread_return_pointer, __th); - *__thread_return = tmp_thread_return_pointer; - - // Print return pointer value to SMACK traces - void *actual_thread_return_pointer = *__thread_return; - - return 0; -} - -void __call_wrapper2(pthread_t *__restrict __newthread, - void *(*__start_routine)(void *), void *__restrict __arg) { - - pthread_t ctid = pthread_self(); - // This next line is commented to demonstrate failure of pthread_join() - // to detect a self-join deadlock when child thread does not wait for - // parent thread to properly record the child thread's ID in the original - // pthread_t struct. - // assume(ctid == *__newthread); - - // I think Zvonimir proposed to just use ctid to index into $pthreadStatus - // This would work in most situations, HOWEVER, what if the parameter __arg - // points to __newthread? Then, __start_routine() could modify this - // object before the parent thread sets __newthread to the ctid. - __SMACK_code("$pthreadStatus[@][0] := $pthread_waiting;", ctid); - - __SMACK_code("$pthreadStatus[@][0] := $pthread_running;", ctid); - __start_routine(__arg); - __SMACK_code("$pthreadStatus[@][0] := $pthread_stopped;", ctid); -} - -int pthread_create2(pthread_t *__restrict __newthread, - __const pthread_attr_t *__restrict __attr, - void *(*__start_routine)(void *), void *__restrict __arg) { - - pthread_t tmp = __VERIFIER_nondet_int(); - - // Add unreachable C-level call to __call_wrapper, so llvm sees - // the call to __call_wrapper and performs DSA on it. - int x = __VERIFIER_nondet_int(); - assume(x == 0); - if (x) - __call_wrapper2(__newthread, __start_routine, __arg); - - __SMACK_code("async call @(@, @, @);", __call_wrapper2, __newthread, - __start_routine, __arg); - __SMACK_code("call @ := corral_getChildThreadID();", tmp); - *__newthread = tmp; - + pthread_create(&tid1, 0, t1, &tid1); return 0; } diff --git a/test/c/failing/regression_525_malloc.c b/test/c/pthread/regression_525_malloc.c similarity index 88% rename from test/c/failing/regression_525_malloc.c rename to test/c/pthread/regression_525_malloc.c index a11c59dc2..62fe5d484 100644 --- a/test/c/failing/regression_525_malloc.c +++ b/test/c/pthread/regression_525_malloc.c @@ -3,8 +3,8 @@ #include // @expect verified -// see: https://github.com/smackers/smack/issues/555 -// https://github.com/smackers/smack/issues/525 + +// see: https://github.com/smackers/smack/issues/525 pthread_t tid1; pthread_t tid2; diff --git a/test/c/pthread/regression_525_malloc_fail.c b/test/c/pthread/regression_525_malloc_fail.c new file mode 100644 index 000000000..09d277211 --- /dev/null +++ b/test/c/pthread/regression_525_malloc_fail.c @@ -0,0 +1,44 @@ +#include "smack.h" +#include +#include + +// @expect error + +// see: https://github.com/smackers/smack/issues/525 + +pthread_t tid1; +pthread_t tid2; + +void *t1(void *arg) { + int *x = malloc(sizeof(*x)); + if (!x) + pthread_exit(NULL); + *x = 1; + pthread_exit(x); + return NULL; +} + +void *t2(void *arg) { + int *y = malloc(sizeof(*y)); + if (!y) + pthread_exit(NULL); + *y = 2; + pthread_exit(y); + return NULL; +} + +int main(void) { + pthread_create(&tid1, 0, t1, 0); + pthread_create(&tid2, 0, t2, 0); + int *tid1_retval; + int *tid2_retval; + pthread_join(tid1, (void **)&tid1_retval); + pthread_join(tid2, (void **)&tid2_retval); + assert(!tid1_retval && *tid1_retval == 1); + assert(!tid2_retval && *tid2_retval == 2); + if (tid1_retval) + free(tid1_retval); + if (tid2_retval) + free(tid2_retval); + return 0; +} diff --git a/test/c/pthread/regression_525_stackalloc.c b/test/c/pthread/regression_525_stackalloc.c index 309bf4f5c..7f293b544 100644 --- a/test/c/pthread/regression_525_stackalloc.c +++ b/test/c/pthread/regression_525_stackalloc.c @@ -4,7 +4,8 @@ #include // @expect verified -// https://github.com/smackers/smack/issues/525 + +// see: https://github.com/smackers/smack/issues/525 struct atomic_var { void *value; diff --git a/test/c/pthread/regression_525_stackalloc_fail.c b/test/c/pthread/regression_525_stackalloc_fail.c new file mode 100644 index 000000000..ddad281d5 --- /dev/null +++ b/test/c/pthread/regression_525_stackalloc_fail.c @@ -0,0 +1,48 @@ +#include "smack.h" +#include +#include +#include + +// @expect error + +// see: https://github.com/smackers/smack/issues/525 + +struct atomic_var { + void *value; +}; + +struct S { + struct atomic_var x; + struct atomic_var y; +}; + +struct T { + int z; +}; + +void atomic_store_ptr(struct atomic_var *var, void *p) { + __atomic_store_n(&var->value, p, __ATOMIC_SEQ_CST); +} + +void *foo(void *arg) { + struct T t = *(struct T *)arg; + return NULL; +} + +int main(void) { + struct S s; + struct T t; + pthread_t thread; + + (*(size_t *)(&s.x.value)) = 0; + s.y.value = NULL; + + if (pthread_create(&thread, NULL, foo, (void *)&t)) + return 1; + + uint64_t v = (size_t)s.x.value; + atomic_store_ptr(&s.y, NULL); + uint64_t w = (size_t)s.x.value; + assert(v != w); + return 0; +} diff --git a/test/c/pthread_extras/config.yml b/test/c/pthread_extras/config.yml index e9e657118..08f7347b5 100644 --- a/test/c/pthread_extras/config.yml +++ b/test/c/pthread_extras/config.yml @@ -1,5 +1,5 @@ +skip: ok +time-limit: 900 verifiers: [corral] memory: [no-reuse-impls] -time-limit: 900 flags: [--pthread, --context-bound=2, --verifier-options=/trackAllVars] -skip: ok diff --git a/test/c/simd/config.yml b/test/c/simd/config.yml index 6bcab82d8..1ef62b3a5 100644 --- a/test/c/simd/config.yml +++ b/test/c/simd/config.yml @@ -1 +1,2 @@ +skip: ok verifiers: [boogie] diff --git a/test/c/special/assume.c b/test/c/special/assume.c index 883e1455a..eee18cc4d 100644 --- a/test/c/special/assume.c +++ b/test/c/special/assume.c @@ -5,8 +5,8 @@ int main(void) { unsigned int y = 2 * (unsigned int)__VERIFIER_nondet_unsigned_short(); - // This assumption is used for verification, even though bit-precise - // is not enabled, the assertion will pass. + // This assumption is used for verification, even though + // integer-encoding=bit-vector is not enabled, the assertion will pass. __builtin_assume((y & 1) == 0); assert((y & 1) == 0); } diff --git a/test/c/special/assume_check.c b/test/c/special/assume_check.c index 9b8afcfa4..d1c2a37f8 100644 --- a/test/c/special/assume_check.c +++ b/test/c/special/assume_check.c @@ -2,11 +2,12 @@ // @expect verified // @flag --llvm-assumes=check -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { unsigned int y = 2 * (unsigned int)__VERIFIER_nondet_unsigned_short(); - // This assumption is checked under bit-precise and is verified. + // This assumption is checked under integer-encoding=bit-vector and is + // verified. __builtin_assume((y & 1) == 0); assert((y & 1) == 0); } diff --git a/test/c/special/assume_check2.c b/test/c/special/assume_check2.c index 031b9c115..9a98a02c6 100644 --- a/test/c/special/assume_check2.c +++ b/test/c/special/assume_check2.c @@ -2,12 +2,13 @@ // @expect verified // @flag --llvm-assumes=check -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { unsigned int y = (2 * (unsigned int)__VERIFIER_nondet_unsigned_short()) + 1; - // This assumption is checked at verification time, and since bit-precise - // is enabled, and y is clearly odd, the check will pass. + // This assumption is checked at verification time, and since + // integer-encoding=bit-vector is enabled, and y is clearly odd, the check + // will pass. __builtin_assume((y & 1) == 1); assert((y & 1) == 1); } diff --git a/test/c/special/assume_check_fail.c b/test/c/special/assume_check_fail.c index 3dbfacd72..f84470d22 100644 --- a/test/c/special/assume_check_fail.c +++ b/test/c/special/assume_check_fail.c @@ -2,12 +2,13 @@ // @expect error // @flag --llvm-assumes=check -// @flag --bit-precise +// @flag --integer-encoding=bit-vector int main(void) { unsigned int y = (2 * (unsigned int)__VERIFIER_nondet_unsigned_short()) + 1; - // This assumption is checked at verification time, and since bit-precise - // is enabled, and y is clearly odd, the assumption should be shown false. + // This assumption is checked at verification time, and since + // integer-encoding=bit-vector is enabled, and y is clearly odd, the + // assumption should be shown false. __builtin_assume((y & 1) == 0); assert((y & 1) == 0); } diff --git a/test/c/special/assume_fail.c b/test/c/special/assume_fail.c index b775dee9c..8ab1c4e1b 100644 --- a/test/c/special/assume_fail.c +++ b/test/c/special/assume_fail.c @@ -5,8 +5,8 @@ int main(void) { unsigned int y = 2 * (unsigned int)__VERIFIER_nondet_unsigned_short(); - // This assumption is not used, and since bit-precise is not enabled, - // verification will fail. + // This assumption is not used, and since integer-encoding=bit-vector is + // not enabled, verification will fail. __builtin_assume((y & 1) == 0); assert((y & 1) == 0); } diff --git a/test/c/special/config.yml b/test/c/special/config.yml new file mode 100644 index 000000000..1576aa77c --- /dev/null +++ b/test/c/special/config.yml @@ -0,0 +1 @@ +skip: ok diff --git a/test/c/strings/strcat_overflow.c b/test/c/strings/strcat_overflow.c index 82b722cdd..98431a560 100644 --- a/test/c/strings/strcat_overflow.c +++ b/test/c/strings/strcat_overflow.c @@ -1,7 +1,7 @@ #include "smack.h" #include -// @flag --memory-safety +// @flag --check=memory-safety // @expect error int main(void) { diff --git a/test/c/strings/strcpy_overflow.c b/test/c/strings/strcpy_overflow.c index 284340248..f89f1c5b8 100644 --- a/test/c/strings/strcpy_overflow.c +++ b/test/c/strings/strcpy_overflow.c @@ -1,7 +1,7 @@ #include "smack.h" #include -// @flag --memory-safety +// @flag --check=memory-safety // @expect error int main(void) { diff --git a/test/config.yml b/test/config.yml index f703d3d0c..4f0a9a2c9 100644 --- a/test/config.yml +++ b/test/config.yml @@ -1,8 +1,8 @@ +skip: false +time-limit: 180 +memory-limit: 450 verifiers: [corral, boogie] memory: [no-reuse-impls, no-reuse, reuse] -time-limit: 120 -memory-limit: 450 flags: [--unroll=2] checkbpl: [] checkout: [] -skip: false diff --git a/test/regtest.py b/test/regtest.py index 3e6a6840f..3f6667806 100755 --- a/test/regtest.py +++ b/test/regtest.py @@ -4,12 +4,10 @@ from multiprocessing.pool import ThreadPool import multiprocessing import os -import signal import logging import yaml import psutil import argparse -from os import path import subprocess import re import glob @@ -24,285 +22,363 @@ 'cplusplus': {'*.cpp'}, 'rust': {'*.rs'}} + def bold(text): - return '\033[1m' + text + '\033[0m' + return '\033[1m' + text + '\033[0m' + def red(text, log_file): - if log_file: - return text - else: - return '\033[0;31m' + text + '\033[0m' + if log_file: + return text + else: + return '\033[0;31m' + text + '\033[0m' + def green(text, log_file): - if log_file: - return text - else: - return '\033[0;32m' + text + '\033[0m' + if log_file: + return text + else: + return '\033[0;32m' + text + '\033[0m' + def get_result(output): - if re.search(r'SMACK timed out', output): - return 'timeout' - elif re.search(r'SMACK found no errors', output): - return 'verified' - elif re.search(r'SMACK found an error', output): - return 'error' - else: - return 'unknown' + if re.search(r'SMACK timed out', output): + return 'timeout' + elif re.search(r'SMACK found no errors', output): + return 'verified' + elif re.search(r'SMACK found an error', output): + return 'error' + else: + return 'unknown' + def merge(metadata, yamldata): - for key in OVERRIDE_FIELDS: - if key in yamldata: - metadata[key] = yamldata[key] + for key in OVERRIDE_FIELDS: + if key in yamldata: + metadata[key] = yamldata[key] + + for key in APPEND_FIELDS: + if key in yamldata: + if key in metadata: + metadata[key] += yamldata[key] + else: + metadata[key] = yamldata[key] - for key in APPEND_FIELDS: - if key in yamldata: - if key in metadata: - metadata[key] += yamldata[key] - else: - metadata[key] = yamldata[key] def metadata(file): - m = {} - prefix = [] + m = {} + prefix = [] - for d in path.dirname(file).split('/'): - prefix += [d] - yaml_file = path.join(*(prefix + ['config.yml'])) - if path.isfile(yaml_file): - with open(yaml_file, "r") as f: - data = yaml.safe_load(f) - merge(m,data) + for d in path.dirname(file).split('/'): + prefix += [d] + yaml_file = path.join(*(prefix + ['config.yml'])) + if path.isfile(yaml_file): + with open(yaml_file, "r") as f: + data = yaml.safe_load(f) + merge(m, data) - with open(file, "r") as f: - for line in f.readlines(): + with open(file, "r") as f: + for line in f.readlines(): - match = re.search(r'@skip', line) - if match: - m['skip'] = True + match = re.search(r'@skip', line) + if match: + m['skip'] = True - match = re.search(r'@flag (.*)',line) - if match: - m['flags'] += shlex.split(match.group(1).strip()) + match = re.search(r'@flag (.*)', line) + if match: + m['flags'] += shlex.split(match.group(1).strip()) - match = re.search(r'@expect (.*)',line) - if match: - m['expect'] = match.group(1).strip() + match = re.search(r'@expect (.*)', line) + if match: + m['expect'] = match.group(1).strip() - match = re.search(r'@checkbpl (.*)', line) - if match: - m['checkbpl'].append(match.group(1).strip()) + match = re.search(r'@checkbpl (.*)', line) + if match: + m['checkbpl'].append(match.group(1).strip()) - match = re.search(r'@checkout (.*)', line) - if match: - m['checkout'].append(match.group(1).strip()) + match = re.search(r'@checkout (.*)', line) + if match: + m['checkout'].append(match.group(1).strip()) - if not m['skip']: - if not 'expect' in m: - print(red("WARNING: @expect MISSING IN %s" % file, None)) - m['expect'] = 'verified' + if not m['skip']: + if 'expect' not in m: + print(red("WARNING: @expect MISSING IN %s" % file, None)) + m['expect'] = 'verified' - if not m['expect'] in ['verified', 'error', 'timeout', 'unknown']: - print(red("WARNING: unexpected @expect annotation '%s'" % m['expect'], None)) + if not m['expect'] in ['verified', 'error', 'timeout', 'unknown']: + print(red("WARNING: unexpected @expect annotation '%s'" % + m['expect'], None)) + + return m - return m # integer constants -PASSED = 0; TIMEDOUT = 1; UNKNOWN = 2; FAILED = -1; -def process_test(cmd, test, memory, verifier, expect, checkbpl, checkout, log_file): - """ - This is the worker function for each process. This function process the supplied - test and returns a tuple containing indicating the test results. - - :return: A tuple with the - """ - str_result = "{0:>20}\n".format(test) - str_result += "{0:>20} {1:>10} :".format(memory, verifier) - - t0 = time.time() - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - universal_newlines=True) - out, err = p.communicate() - elapsed = time.time() - t0 - status = 0 - - bplfile = cmd[cmd.index('-bpl')+1] - with open(os.devnull, 'w') as devnull: - for f in checkbpl: - with open(bplfile) as bpl: - checker = subprocess.Popen(shlex.split(f), stdin=bpl, stdout=devnull, stderr=devnull) - checker.wait() - status = status or checker.returncode - - for f in checkout: - checker = subprocess.Popen(shlex.split(f), stdin=subprocess.PIPE, stdout=devnull, stderr=devnull) - checker.communicate(input=out) - status = status or checker.returncode - - # get the test results - result = get_result(out+err) - if result == expect and status == 0: - str_result += green('PASSED ', log_file) - elif result == 'timeout': - str_result += red('TIMEOUT', log_file) - elif result == 'unknown': - str_result += red('UNKNOWN', log_file) - else: - str_result += red('FAILED ', log_file) - - str_result += ' [%.2fs]' % round(elapsed, 2) - return str_result +PASSED = 0 +TIMEDOUT = 1 +UNKNOWN = 2 +FAILED = -1 + + +def process_test( + cmd, + test, + memory, + verifier, + expect, + checkbpl, + checkout, + log_file): + """ + This is the worker function for each process. This function process the + supplied test and returns a tuple containing indicating the test results. + + :return: A tuple with the + """ + str_result = "{0:>20}\n".format(test) + str_result += "{0:>20} {1:>10} :".format(memory, verifier) + + t0 = time.time() + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True) + out, err = p.communicate() + elapsed = time.time() - t0 + status = 0 + + bplfile = cmd[cmd.index('-bpl') + 1] + with open(os.devnull, 'w') as devnull: + for f in checkbpl: + with open(bplfile) as bpl: + checker = subprocess.Popen( + shlex.split(f), stdin=bpl, stdout=devnull, stderr=devnull) + checker.wait() + status = status or checker.returncode + + for f in checkout: + checker = subprocess.Popen( + shlex.split(f), + stdin=subprocess.PIPE, + stdout=devnull, + stderr=devnull) + checker.communicate(input=out.encode()) + status = status or checker.returncode + + # get the test results + result = get_result(out + err) + if result == expect and status == 0: + str_result += green('PASSED ', log_file) + elif result == 'timeout': + str_result += red('TIMEOUT', log_file) + elif result == 'unknown': + str_result += red('UNKNOWN', log_file) + else: + str_result += red('FAILED ', log_file) + + str_result += ' [%.2fs]' % round(elapsed, 2) + return str_result + passed = failed = timeouts = unknowns = 0 + + def tally_result(result): - """ - Tallies the result of each worker. This will only be called by the main thread. - """ - # log the info - logging.info(result) - - global passed, failed, timeouts, unknowns - if "PASSED" in result: - passed += 1 - elif "FAILED" in result: - failed += 1 - elif "TIMEOUT" in result: - timeouts += 1 - elif "UNKNOWN" in result: - unknowns += 1 + """ + Tallies the result of each worker. This will only be called by the main + thread. + """ + # log the info + logging.info(result) + + global passed, failed, timeouts, unknowns + if "PASSED" in result: + passed += 1 + elif "FAILED" in result: + failed += 1 + elif "TIMEOUT" in result: + timeouts += 1 + elif "UNKNOWN" in result: + unknowns += 1 + def get_extensions(languages): - languages = list(languages.split(',')) - extensions = set() - for language in languages: - extensions |= LANGUAGES[language] - return extensions + languages = list(languages.split(',')) + extensions = set() + for language in languages: + extensions |= LANGUAGES[language] + return extensions + def get_tests(folder, extensions): - tests = [] - for ext in extensions: - tests_path = path.dirname(__file__) - tests.extend(glob.glob(path.join(tests_path, folder, ext))) - tests.sort() - return tests + tests = [] + for ext in extensions: + tests_path = path.dirname(__file__) + tests.extend(glob.glob(path.join(tests_path, folder, ext))) + tests.sort() + return tests + def main(): - """ - Main entry point for the test suite. - """ - t0 = time.time() - num_cpus = multiprocessing.cpu_count() - mem_total = psutil.virtual_memory().total / (1024 * 1024) - - # configure the CLI - parser = argparse.ArgumentParser() - parser.add_argument("--exhaustive", help="check all configurations on all examples", action="store_true") - parser.add_argument("--all-configs", help="check all configurations per example", action="store_true") - parser.add_argument("--all-examples", help="check all examples", action="store_true") - parser.add_argument("--folder", action="store", default="**/**", type=str, - help="sets the regressions folder to run") - parser.add_argument("--threads", action="store", dest="n_threads", default=num_cpus, type=int, - help="execute regressions using the selected number of threads in parallel") - parser.add_argument("--log", action="store", dest="log_level", default="DEBUG", type=str, - help="sets the logging level (DEBUG, INFO, WARNING)") - parser.add_argument("--output-log", action="store", dest="log_path", type=str, - help="sets the output log path. (std out by default)") - parser.add_argument("--languages", action="store", default="c", choices=list(LANGUAGES.keys()), - help="Comma separated list of langauges to test. C[c],C++[cplusplus],Rust[rust]") - args = parser.parse_args() - - if args.exhaustive: - args.all_examples = True; - args.all_configs = True; - - extensions = get_extensions(args.languages) - tests = get_tests(args.folder, extensions) - - # configure the logging - log_format = '' - log_level = logging.DEBUG - - # add more log levels later (if needed) - if args.log_level.upper() == "INFO": - log_level = logging.INFO - elif args.log_level.upper() == "WARNING": - log_level = logging.WARNING - - # if the user supplied a log path, write the logs to that file. - # otherwise, write the logs to std out. - if args.log_path: - logging.basicConfig(filename=args.log_path, format=log_format, level=log_level) - else: - logging.basicConfig(format=log_format, level=log_level) - - logging.debug("Creating Pool with '%d' Workers" % args.n_threads) - p = ThreadPool(processes=args.n_threads) - - try: - # start the tests - logging.info("Running regression tests...") - - # start processing the tests. - - results = [] - for test in tests: - # get the meta data for this test - meta = metadata(test) - - if meta['memory-limit'] > mem_total: - continue - - if meta['skip'] == True: - continue - - if meta['skip'] != False and not args.all_examples: - continue - - # build up the subprocess command - cmd = ['smack', test] - cmd += ['--time-limit', str(meta['time-limit'])] - cmd += meta['flags'] - - for memory in meta['memory'][:100 if args.all_configs else 1]: - cmd += ['--mem-mod=' + memory] - - for verifier in meta['verifiers'][:100 if args.all_configs else 1]: - name = path.splitext(path.basename(test))[0] - cmd += ['--verifier=' + verifier] - cmd += ['-bc', "%s-%s-%s.bc" % (name, memory, verifier)] - cmd += ['-bpl', "%s-%s-%s.bpl" % (name, memory, verifier)] - r = p.apply_async(process_test, - args=(cmd[:], test, memory, verifier, meta['expect'], meta['checkbpl'], meta['checkout'], args.log_path,), - callback=tally_result) - results.append(r) - - # keep the main thread active while there are active workers - for r in results: - r.wait() - - except KeyboardInterrupt: - logging.debug("Caught KeyboardInterrupt, terminating workers") - p.terminate() # terminate any remaining workers - p.join() - else: - logging.debug("Quitting normally") - # close the pool. this prevents any more tasks from being submitted. - p.close() - p.join() # wait for all workers to finish their tasks - - # log the elapsed time - elapsed_time = time.time() - t0 - logging.info(' ELAPSED TIME [%.2fs]' % round(elapsed_time, 2)) - - # log the test results - logging.info(' PASSED count: %d' % passed) - logging.info(' FAILED count: %d' % failed) - logging.info(' TIMEOUT count: %d' % timeouts) - logging.info(' UNKNOWN count: %d' % unknowns) - - # if there are any failed tests or tests that timed out, set the system - # exit code to a failure status - if timeouts > 0 or failed > 0 or unknowns > 0: - sys.exit(1) - -if __name__=="__main__": - main() + """ + Main entry point for the test suite. + """ + t0 = time.time() + num_cpus = multiprocessing.cpu_count() + mem_total = psutil.virtual_memory().total / (1024 * 1024) + + # configure the CLI + parser = argparse.ArgumentParser() + parser.add_argument( + "--exhaustive", + help="check all configurations on all examples", + action="store_true") + parser.add_argument( + "--all-configs", + help="check all configurations per example", + action="store_true") + parser.add_argument( + "--all-examples", + help="check all examples", + action="store_true") + parser.add_argument("--folder", action="store", default="**/**", type=str, + help="sets the regressions folder to run") + parser.add_argument( + "--threads", + action="store", + dest="n_threads", + default=num_cpus, + type=int, + help='''execute regressions using the selected number of threads in + parallel''') + parser.add_argument( + "--log", + action="store", + dest="log_level", + default="DEBUG", + type=str, + help="sets the logging level (DEBUG, INFO, WARNING)") + parser.add_argument( + "--output-log", + action="store", + dest="log_path", + type=str, + help="sets the output log path. (std out by default)") + parser.add_argument( + "--languages", + action="store", + default="c", + choices=list( + LANGUAGES.keys()), + help='''Comma separated list of langauges to test. C[c],C++[cplusplus], + Rust[rust]''') + args = parser.parse_args() + + if args.exhaustive: + args.all_examples = True + args.all_configs = True + + extensions = get_extensions(args.languages) + tests = get_tests(args.folder, extensions) + + # configure the logging + log_format = '' + log_level = logging.DEBUG + + # add more log levels later (if needed) + if args.log_level.upper() == "INFO": + log_level = logging.INFO + elif args.log_level.upper() == "WARNING": + log_level = logging.WARNING + + # if the user supplied a log path, write the logs to that file. + # otherwise, write the logs to std out. + if args.log_path: + logging.basicConfig( + filename=args.log_path, + format=log_format, + level=log_level) + else: + logging.basicConfig(format=log_format, level=log_level) + + logging.debug("Creating Pool with '%d' Workers" % args.n_threads) + p = ThreadPool(processes=args.n_threads) + + try: + # start the tests + logging.info("Running regression tests...") + + # start processing the tests. + + results = [] + for test in tests: + # get the meta data for this test + meta = metadata(test) + + if meta['memory-limit'] > mem_total: + continue + + if meta['skip'] is True: + continue + + if meta['skip'] is not False and not args.all_examples: + continue + + # build up the subprocess command + cmd = ['smack', test] + cmd += ['--time-limit', str(meta['time-limit'])] + cmd += meta['flags'] + + for memory in meta['memory'][:100 if args.all_configs else 1]: + cmd += ['--mem-mod=' + memory] + + for verifier in (meta['verifiers'] + [:100 if args.all_configs else 1]): + name = path.splitext(path.basename(test))[0] + cmd += ['--verifier=' + verifier] + cmd += ['-bc', "%s-%s-%s.bc" % (name, memory, verifier)] + cmd += ['-bpl', "%s-%s-%s.bpl" % (name, memory, verifier)] + r = p.apply_async( + process_test, + args=( + cmd[:], + test, + memory, + verifier, + meta['expect'], + meta['checkbpl'], + meta['checkout'], + args.log_path, + ), + callback=tally_result) + results.append(r) + + # keep the main thread active while there are active workers + for r in results: + r.wait() + + except KeyboardInterrupt: + logging.debug("Caught KeyboardInterrupt, terminating workers") + p.terminate() # terminate any remaining workers + p.join() + else: + logging.debug("Quitting normally") + # close the pool. this prevents any more tasks from being submitted. + p.close() + p.join() # wait for all workers to finish their tasks + + # log the elapsed time + elapsed_time = time.time() - t0 + logging.info(' ELAPSED TIME [%.2fs]' % round(elapsed_time, 2)) + + # log the test results + logging.info(' PASSED count: %d' % passed) + logging.info(' FAILED count: %d' % failed) + logging.info(' TIMEOUT count: %d' % timeouts) + logging.info(' UNKNOWN count: %d' % unknowns) + + # if there are any failed tests or tests that timed out, set the system + # exit code to a failure status + if timeouts > 0 or failed > 0 or unknowns > 0: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/test/rust/array/array.rs b/test/rust/array/array.rs new file mode 100644 index 000000000..007e325b2 --- /dev/null +++ b/test/rust/array/array.rs @@ -0,0 +1,19 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +fn main() { + let ar = [ + 2u8.verifier_nondet(), + 3u8.verifier_nondet(), + 4u8.verifier_nondet(), + ]; + let idx = 1usize.verifier_nondet(); + smack::assume!(ar[0] < 4); + smack::assume!(ar[1] < 5); + smack::assume!(ar[2] < 6); + smack::assume!(idx < 3); + smack::assert!(ar[idx] <= 5); +} diff --git a/test/rust/array/array_fail.rs b/test/rust/array/array_fail.rs new file mode 100644 index 000000000..2e1318da3 --- /dev/null +++ b/test/rust/array/array_fail.rs @@ -0,0 +1,19 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +fn main() { + let ar = [ + 2u8.verifier_nondet(), + 3u8.verifier_nondet(), + 4u8.verifier_nondet(), + ]; + let idx = 1usize.verifier_nondet(); + smack::assume!(ar[0] < 4); + smack::assume!(ar[1] < 5); + smack::assume!(ar[2] < 6); + smack::assume!(idx < 3); + smack::assert!(ar[idx] <= 4); +} diff --git a/test/rust/array/array_len.rs b/test/rust/array/array_len.rs new file mode 100644 index 000000000..9f8cb3f79 --- /dev/null +++ b/test/rust/array/array_len.rs @@ -0,0 +1,18 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +fn main() { + let ar = [ + 2u8.verifier_nondet(), + 3u8.verifier_nondet(), + 4u8.verifier_nondet(), + ]; + let idx = 5usize.verifier_nondet(); + smack::assume!(idx < 3); + let fh = &ar[..idx]; + let sh = &ar[idx..]; + smack::assert!(fh.len() + sh.len() == ar.len()); +} diff --git a/test/rust/array/array_len_fail.rs b/test/rust/array/array_len_fail.rs new file mode 100644 index 000000000..3ce39624f --- /dev/null +++ b/test/rust/array/array_len_fail.rs @@ -0,0 +1,18 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +fn main() { + let ar = [ + 2u8.verifier_nondet(), + 3u8.verifier_nondet(), + 4u8.verifier_nondet(), + ]; + let idx = 5usize.verifier_nondet(); + smack::assume!(idx < 3); + let fh = &ar[..idx]; + let sh = &ar[idx..]; + smack::assert!(fh.len() + sh.len() < ar.len()); +} diff --git a/test/rust/array/array_slices.rs b/test/rust/array/array_slices.rs new file mode 100644 index 000000000..116624619 --- /dev/null +++ b/test/rust/array/array_slices.rs @@ -0,0 +1,21 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +fn main() { + let mut ar = [ + 2u8.verifier_nondet(), + 3u8.verifier_nondet(), + 4u8.verifier_nondet(), + ]; + let idx = 1usize.verifier_nondet(); + smack::assume!(ar[0] < 4); + smack::assume!(ar[1] < 5); + smack::assume!(ar[2] < 6); + smack::assume!(idx < 3 && idx > 1); + let slice = &mut ar[..idx]; + slice[1] += 1; + smack::assert!(!slice.is_empty() && slice[1] <= 5); +} diff --git a/test/rust/array/array_slices_fail.rs b/test/rust/array/array_slices_fail.rs new file mode 100644 index 000000000..bafe02cfb --- /dev/null +++ b/test/rust/array/array_slices_fail.rs @@ -0,0 +1,21 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +fn main() { + let mut ar = [ + 2u8.verifier_nondet(), + 3u8.verifier_nondet(), + 4u8.verifier_nondet(), + ]; + let idx = 1usize.verifier_nondet(); + smack::assume!(ar[0] < 4); + smack::assume!(ar[1] < 5); + smack::assume!(ar[2] < 6); + smack::assume!(idx < 3 && idx > 1); + let slice = &mut ar[..idx]; + slice[1] += 1; + smack::assert!(slice.is_empty() || slice[1] < 5); +} diff --git a/test/rust/basic/add_fail.rs b/test/rust/basic/add_fail.rs index 3f32c4834..9a1f74226 100644 --- a/test/rust/basic/add_fail.rs +++ b/test/rust/basic/add_fail.rs @@ -1,11 +1,11 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect error fn main() { - let a = 2; - let b = 3; - assert!(a+b != 5); + let a = 2; + let b = 3; + smack::assert!(a + b != 5); } diff --git a/test/rust/basic/add_overflow.rs b/test/rust/basic/add_overflow.rs index aa29698fe..36e5dd4e0 100644 --- a/test/rust/basic/add_overflow.rs +++ b/test/rust/basic/add_overflow.rs @@ -1,12 +1,12 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; -// @flag --integer-overflow +// @flag --check=integer-overflow // @expect error fn main() { - let a: u8 = 128; - let b: u8 = 128; - let c = a + b; + let a = 200u8.verifier_nondet(); + let b = 56u8.verifier_nondet(); + let c = a + b; } diff --git a/test/rust/basic/arith.rs b/test/rust/basic/arith.rs index 94075f3fc..3c29b1d67 100644 --- a/test/rust/basic/arith.rs +++ b/test/rust/basic/arith.rs @@ -1,54 +1,54 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect verified fn main() { - // unsigned - { - let a: u32 = 2; - let b: u32 = 3; - { - let c = a + b; - assert!(c == 5); - } - { - let c = a * b; - assert!(c == 6); - } - { - let c = b - a; - assert!(c == 1); - } - { - let c = a % b; - assert!(c == 2); - let d = b % a; - assert!(d == 1); - } - { - let c = a / b; - assert!(c == 0); - let d = b / a; - assert!(d == 1); - } - } - // signed - { - let a: i32 = -3; - let b: i32 = 5; - { - let c = a + b; - assert!(c == 2); - } - { - let c = a * b; - assert!(c == -15); - } - { - let c = b - a; - assert!(c == 8); + // unsigned + { + let a: u32 = 2; + let b: u32 = 3; + { + let c = a + b; + smack::assert!(c == 5); + } + { + let c = a * b; + smack::assert!(c == 6); + } + { + let c = b - a; + smack::assert!(c == 1); + } + { + let c = a % b; + smack::assert!(c == 2); + let d = b % a; + smack::assert!(d == 1); + } + { + let c = a / b; + smack::assert!(c == 0); + let d = b / a; + smack::assert!(d == 1); + } + } + // signed + { + let a: i32 = -3; + let b: i32 = 5; + { + let c = a + b; + smack::assert!(c == 2); + } + { + let c = a * b; + smack::assert!(c == -15); + } + { + let c = b - a; + smack::assert!(c == 8); + } } - } } diff --git a/test/rust/basic/arith_assume.rs b/test/rust/basic/arith_assume.rs index 6a2d1d381..d0db26768 100644 --- a/test/rust/basic/arith_assume.rs +++ b/test/rust/basic/arith_assume.rs @@ -1,13 +1,13 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect verified fn main() { - let a = 6i32.nondet(); - let b = 7i32.nondet(); - assume!(4 < a && a < 8); // a in [5,7] - assume!(5 < b && b < 9); // b in [6,8] - assert!(30 <= a * b && a * b <= 56); // a*b in [30,56] + let a = 6i32.verifier_nondet(); + let b = 7i32.verifier_nondet(); + smack::assume!(4 < a && a < 8); // a in [5,7] + smack::assume!(5 < b && b < 9); // b in [6,8] + smack::assert!(30 <= a * b && a * b <= 56); // a*b in [30,56] } diff --git a/test/rust/basic/arith_assume2.rs b/test/rust/basic/arith_assume2.rs index 6a2d1d381..d0db26768 100644 --- a/test/rust/basic/arith_assume2.rs +++ b/test/rust/basic/arith_assume2.rs @@ -1,13 +1,13 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect verified fn main() { - let a = 6i32.nondet(); - let b = 7i32.nondet(); - assume!(4 < a && a < 8); // a in [5,7] - assume!(5 < b && b < 9); // b in [6,8] - assert!(30 <= a * b && a * b <= 56); // a*b in [30,56] + let a = 6i32.verifier_nondet(); + let b = 7i32.verifier_nondet(); + smack::assume!(4 < a && a < 8); // a in [5,7] + smack::assume!(5 < b && b < 9); // b in [6,8] + smack::assert!(30 <= a * b && a * b <= 56); // a*b in [30,56] } diff --git a/test/rust/basic/arith_assume_fail.rs b/test/rust/basic/arith_assume_fail.rs index 554b3e108..f179bf657 100644 --- a/test/rust/basic/arith_assume_fail.rs +++ b/test/rust/basic/arith_assume_fail.rs @@ -1,16 +1,17 @@ #[macro_use] -mod smack; +extern crate smack; +use smack::assert; use smack::*; // @expect error fn main() { - let a = 6i32.nondet(); - let b = 7i32.nondet(); - assume!(4 < a && a < 8); // a in [5,7] - assume!(5 < b && b < 9); // b in [6,8] - let x = a * b; - assert!(!(x==30 || x==35 || x==40 || - x==36 || x==48 || x==42 || - x==49 || x==56)); // a*b != anything allowed + let a = 6i32.verifier_nondet(); + let b = 7i32.verifier_nondet(); + smack::assume!(4 < a && a < 8); // a in [5,7] + smack::assume!(5 < b && b < 9); // b in [6,8] + let x = a * b; + smack::assert!( + !(x == 30 || x == 35 || x == 40 || x == 36 || x == 48 || x == 42 || x == 49 || x == 56) + ); // a*b != anything allowed } diff --git a/test/rust/basic/arith_assume_verifier.rs b/test/rust/basic/arith_assume_verifier.rs new file mode 100644 index 000000000..f1fa7e405 --- /dev/null +++ b/test/rust/basic/arith_assume_verifier.rs @@ -0,0 +1,13 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +fn main() { + let a = 6i32.verifier_nondet(); + let b = 7i32.verifier_nondet(); + verifier_assume!(4 < a && a < 8); // a in [5,7] + verifier_assume!(5 < b && b < 9); // b in [6,8] + verifier_assert!(30 <= a * b && a * b <= 56); // a*b in [30,56] +} diff --git a/test/rust/basic/arith_assume_verifier_fail.rs b/test/rust/basic/arith_assume_verifier_fail.rs new file mode 100644 index 000000000..3a3dff12e --- /dev/null +++ b/test/rust/basic/arith_assume_verifier_fail.rs @@ -0,0 +1,16 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +fn main() { + let a = 6i32.verifier_nondet(); + let b = 7i32.verifier_nondet(); + verifier_assume!(4 < a && a < 8); // a in [5,7] + verifier_assume!(5 < b && b < 9); // b in [6,8] + let x = a * b; + verifier_assert!( + !(x == 30 || x == 35 || x == 40 || x == 36 || x == 48 || x == 42 || x == 49 || x == 56) + ); // a*b != anything allowed +} diff --git a/test/rust/basic/div_fail.rs b/test/rust/basic/div_fail.rs index 812120970..b8ac98d01 100644 --- a/test/rust/basic/div_fail.rs +++ b/test/rust/basic/div_fail.rs @@ -1,11 +1,11 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect error fn main() { - let a = 2; - let b = 3; - assert!(b/a != 1); + let a = 2; + let b = 3; + smack::assert!(b / a != 1); } diff --git a/test/rust/basic/mod_fail.rs b/test/rust/basic/mod_fail.rs index 89522cc79..4ee708dd1 100644 --- a/test/rust/basic/mod_fail.rs +++ b/test/rust/basic/mod_fail.rs @@ -1,11 +1,11 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect error fn main() { - let a = 2; - let b = 3; - assert!(b%a != 1); + let a = 2; + let b = 3; + smack::assert!(b % a != 1); } diff --git a/test/rust/basic/mul_fail.rs b/test/rust/basic/mul_fail.rs index 82638cfc4..b32073365 100644 --- a/test/rust/basic/mul_fail.rs +++ b/test/rust/basic/mul_fail.rs @@ -1,11 +1,11 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect error fn main() { - let a = 2; - let b = 3; - assert!(b*a != 6); + let a = 2; + let b = 3; + smack::assert!(b * a != 6); } diff --git a/test/rust/basic/mul_overflow.rs b/test/rust/basic/mul_overflow.rs index 9d1c11f05..f97ce5d78 100644 --- a/test/rust/basic/mul_overflow.rs +++ b/test/rust/basic/mul_overflow.rs @@ -1,12 +1,12 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; -// @flag --integer-overflow +// @flag --check=integer-overflow // @expect error fn main() { - let a: u8 = 128; - let b: u8 = 2; - let c = a * b; + let a = 32u8.verifier_nondet(); + let b = 8u8.verifier_nondet(); + let c = a * b; } diff --git a/test/rust/basic/nondet_defined.rs b/test/rust/basic/nondet_defined.rs new file mode 100644 index 000000000..9dd771cad --- /dev/null +++ b/test/rust/basic/nondet_defined.rs @@ -0,0 +1,11 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +fn main() { + // Verifies that nondet is defined in the ll file. + let a = 6u32.verifier_nondet(); + verifier_assert!(a >= 0); // a is unsigned +} diff --git a/test/rust/basic/print.rs b/test/rust/basic/print.rs new file mode 100644 index 000000000..ecba26f82 --- /dev/null +++ b/test/rust/basic/print.rs @@ -0,0 +1,34 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +static mut NUM: i32 = 0; + +fn incr(x: i32) -> i32 { + unsafe { + NUM += x; + NUM + } +} + +fn test_print() { + print!("hola"); + println!("hola"); + print!("hola, senor {}", incr(1)); + println!("hola, senor {}", incr(2)); + print!("hola, senor {0} and senor {1}", 3, incr(3)); + println!("hola, senor {0} and senor {1}", 4, incr(4)); + eprint!("hola"); + eprintln!("hola"); + eprint!("hola, senor {}", incr(1)); + eprintln!("hola, senor {}", incr(2)); + eprint!("hola, senor {0} and senor {1}", 3, incr(3)); + eprintln!("hola, senor {0} and senor {1}", 4, incr(4)); +} + +fn main() { + test_print(); + smack::assert!(NUM == 0 + 2 + 4 + 6 + 8); +} diff --git a/test/rust/basic/print_fail.rs b/test/rust/basic/print_fail.rs new file mode 100644 index 000000000..e2b79ec37 --- /dev/null +++ b/test/rust/basic/print_fail.rs @@ -0,0 +1,34 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +static mut NUM: i32 = 0; + +fn incr(x: i32) -> i32 { + unsafe { + NUM += x; + NUM + } +} + +fn test_print() { + print!("hola"); + println!("hola"); + print!("hola, senor {}", incr(1)); + println!("hola, senor {}", incr(2)); + print!("hola, senor {0} and senor {1}", 3, incr(3)); + println!("hola, senor {0} and senor {1}", 4, incr(4)); + eprint!("hola"); + eprintln!("hola"); + eprint!("hola, senor {}", incr(1)); + eprintln!("hola, senor {}", incr(2)); + eprint!("hola, senor {0} and senor {1}", 3, incr(3)); + eprintln!("hola, senor {0} and senor {1}", 4, incr(4)); +} + +fn main() { + test_print(); + smack::assert!(NUM != 0 + 2 + 4 + 6 + 8); +} diff --git a/test/rust/basic/sub_fail.rs b/test/rust/basic/sub_fail.rs index b32beb617..1582b0ce4 100644 --- a/test/rust/basic/sub_fail.rs +++ b/test/rust/basic/sub_fail.rs @@ -1,11 +1,11 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect error fn main() { - let a = 2; - let b = 3; - assert!(b-a != 1); + let a = 2; + let b = 3; + smack::assert!(b - a != 1); } diff --git a/test/rust/basic/sub_overflow.rs b/test/rust/basic/sub_overflow.rs index 3128212d9..d6b916375 100644 --- a/test/rust/basic/sub_overflow.rs +++ b/test/rust/basic/sub_overflow.rs @@ -1,12 +1,12 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; -// @flag --integer-overflow +// @flag --check=integer-overflow // @expect error fn main() { - let a: u8 = 128; - let b: u8 = 129; - let c = a - b; + let a = 128u8.verifier_nondet(); + let b = 129u8.verifier_nondet(); + let c = a - b; } diff --git a/test/rust/basic/tuple.rs b/test/rust/basic/tuple.rs new file mode 100644 index 000000000..ad1515b02 --- /dev/null +++ b/test/rust/basic/tuple.rs @@ -0,0 +1,13 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +fn main() { + let t = (2u8.verifier_nondet(), 3u8.verifier_nondet()); + let (a, b) = t; + smack::assume!(a < 4); + smack::assume!(b < 5); + smack::assert!(t.0 + t.1 <= 7); +} diff --git a/test/rust/basic/tuple_fail.rs b/test/rust/basic/tuple_fail.rs new file mode 100644 index 000000000..6b59e29b7 --- /dev/null +++ b/test/rust/basic/tuple_fail.rs @@ -0,0 +1,13 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +fn main() { + let t = (2u8.verifier_nondet(), 3u8.verifier_nondet()); + let (a, b) = t; + smack::assume!(a < 4); + smack::assume!(b < 5); + smack::assert!(t.0 + t.1 < 7); +} diff --git a/test/rust/box/box_basic.rs b/test/rust/box/box_basic.rs new file mode 100644 index 000000000..6811ff6b7 --- /dev/null +++ b/test/rust/box/box_basic.rs @@ -0,0 +1,11 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +fn main() { + let b1: Box = Box::new(1); + let b2: Box = Box::new(2); + smack::assert!(*b1 != *b2); +} diff --git a/test/rust/box/box_basic_fail.rs b/test/rust/box/box_basic_fail.rs new file mode 100644 index 000000000..b64fc709f --- /dev/null +++ b/test/rust/box/box_basic_fail.rs @@ -0,0 +1,11 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +fn main() { + let b1: Box = Box::new(1); + let b2: Box = Box::new(2); + smack::assert!(*b1 == *b2); +} diff --git a/test/rust/failing/array_slices_cmp.rs b/test/rust/failing/array_slices_cmp.rs new file mode 100644 index 000000000..1ed308add --- /dev/null +++ b/test/rust/failing/array_slices_cmp.rs @@ -0,0 +1,20 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// see: https://github.com/smackers/smack/issues/575 +// @expect verified + +fn main() { + let ar = [ + 2u8.verifier_nondet(), + 3u8.verifier_nondet(), + 4u8.verifier_nondet(), + 5u8.verifier_nondet(), + ]; + smack::assume!(ar[0] == ar[2]); + smack::assume!(ar[1] == ar[3]); + let fh = &ar[0..2]; + let sh = &ar[2..4]; + smack::assert!(fh == sh); +} diff --git a/test/rust/failing/array_slices_cmp_fail.rs b/test/rust/failing/array_slices_cmp_fail.rs new file mode 100644 index 000000000..69175e218 --- /dev/null +++ b/test/rust/failing/array_slices_cmp_fail.rs @@ -0,0 +1,20 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// see: https://github.com/smackers/smack/issues/575 +// @expect error + +fn main() { + let ar = [ + 2u8.verifier_nondet(), + 3u8.verifier_nondet(), + 4u8.verifier_nondet(), + 5u8.verifier_nondet(), + ]; + smack::assume!(ar[0] == ar[2]); + smack::assume!(ar[1] == ar[3]); + let fh = &ar[0..2]; + let sh = &ar[2..4]; + smack::assert!(fh != sh); +} diff --git a/test/rust/failing/config.yml b/test/rust/failing/config.yml new file mode 100644 index 000000000..2d3fea097 --- /dev/null +++ b/test/rust/failing/config.yml @@ -0,0 +1 @@ +skip: true diff --git a/test/rust/failing/gauss_sum_nondet.rs b/test/rust/failing/gauss_sum_nondet.rs new file mode 100644 index 000000000..af51271b8 --- /dev/null +++ b/test/rust/failing/gauss_sum_nondet.rs @@ -0,0 +1,16 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @flag --unroll=4 --time-limit=480 +// @expect verified + +fn main() { + let mut sum = 0; + let b = 7u64.verifier_nondet(); + smack::assume!(b > 2); + for i in 0..b as u64 { + sum += i; + } + smack::assert!(2 * sum == b * (b - 1)); +} diff --git a/test/rust/failing/gauss_sum_nondet_fail.rs b/test/rust/failing/gauss_sum_nondet_fail.rs new file mode 100644 index 000000000..00f6a4e74 --- /dev/null +++ b/test/rust/failing/gauss_sum_nondet_fail.rs @@ -0,0 +1,16 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @flag --unroll=4 --time-limit=480 +// @expect error + +fn main() { + let mut sum = 0; + let b = 7u64.verifier_nondet(); + smack::assume!(b > 2); + for i in 0..b as u64 { + sum += i; + } + smack::assert!(2 * sum != b * (b - 1)); +} diff --git a/test/rust/failing/option.rs b/test/rust/failing/option.rs new file mode 100644 index 000000000..6c4993a7b --- /dev/null +++ b/test/rust/failing/option.rs @@ -0,0 +1,28 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +fn safe_div(x: u64, y: u64) -> Option { + if y != 0 { + Some(x / y) + } else { + None + } +} + +fn main() { + let x = 2u64.verifier_nondet(); + smack::assume!(x > 0); + let a = safe_div(2 * x, x); + match a { + Some(x) => smack::assert!(x == 2), + None => smack::assert!(false), + }; + let b = safe_div(x, 0); + match b { + Some(x) => smack::assert!(false), + None => smack::assert!(true), + }; +} diff --git a/test/rust/failing/option_fail.rs b/test/rust/failing/option_fail.rs new file mode 100644 index 000000000..abfe5e839 --- /dev/null +++ b/test/rust/failing/option_fail.rs @@ -0,0 +1,28 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +fn safe_div(x: u64, y: u64) -> Option { + if y != 0 { + Some(x / y) + } else { + None + } +} + +fn main() { + let x = 2u64.verifier_nondet(); + smack::assume!(x > 0); + let a = safe_div(2 * x, x); + match a { + Some(x) => smack::assert!(x == 2), + None => smack::assert!(false), + }; + let b = safe_div(x, 0); + match b { + Some(x) => smack::assert!(true), + None => smack::assert!(false), // Division by zero should return None + }; +} diff --git a/test/rust/failing/string_basic.rs b/test/rust/failing/string_basic.rs new file mode 100644 index 000000000..062f17e04 --- /dev/null +++ b/test/rust/failing/string_basic.rs @@ -0,0 +1,10 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +fn main() { + let s = String::from("Hello, world!"); + smack::assert!(s.capacity() >= 5); +} diff --git a/test/rust/failing/string_basic_fail.rs b/test/rust/failing/string_basic_fail.rs new file mode 100644 index 000000000..836be9bba --- /dev/null +++ b/test/rust/failing/string_basic_fail.rs @@ -0,0 +1,10 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +fn main() { + let s = String::from("Hello, world!"); + smack::assert!(s.capacity() < 5); +} diff --git a/test/rust/failing/vec_resize.rs b/test/rust/failing/vec_resize.rs new file mode 100644 index 000000000..634878e0e --- /dev/null +++ b/test/rust/failing/vec_resize.rs @@ -0,0 +1,13 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @flag --unroll=3 --time-limit=480 +// @expect verified + +fn main() { + let mut v1: Vec = vec![0]; + let mut v2: Vec = vec![3]; + v1.append(&mut v2); + smack::assert!(v1[1] == 3); +} diff --git a/test/rust/failing/vec_resize_fail.rs b/test/rust/failing/vec_resize_fail.rs new file mode 100644 index 000000000..40a7f6640 --- /dev/null +++ b/test/rust/failing/vec_resize_fail.rs @@ -0,0 +1,13 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @flag --unroll=3 --time-limit=480 +// @expect error + +fn main() { + let mut v1: Vec = vec![0]; + let mut v2: Vec = vec![3]; + v1.append(&mut v2); + smack::assert!(v1[1] != 3); +} diff --git a/test/rust/functions/closure.rs b/test/rust/functions/closure.rs index e20a855db..a8c103051 100644 --- a/test/rust/functions/closure.rs +++ b/test/rust/functions/closure.rs @@ -1,23 +1,24 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect verified fn call_with_one(mut some_closure: F) -> () - where F : FnMut(i32) -> () { - - some_closure(1); +where + F: FnMut(i32) -> (), +{ + some_closure(1); } fn main() { - let mut num = 5i32.nondet(); - let original_num = num; - { - let mut add_num = |x: i32| num += x; + let mut num = 5i32.verifier_nondet(); + let original_num = num; + { + let mut add_num = |x: i32| num += x; - add_num(5); - call_with_one(&mut add_num); - } - assert_eq!(original_num + 6, num); + add_num(5); + call_with_one(&mut add_num); + } + smack::assert_eq!(original_num + 6, num); } diff --git a/test/rust/functions/closure_fail.rs b/test/rust/functions/closure_fail.rs index 40ca9948e..7a300c804 100644 --- a/test/rust/functions/closure_fail.rs +++ b/test/rust/functions/closure_fail.rs @@ -1,23 +1,24 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect error fn call_with_one(mut some_closure: F) -> () - where F : FnMut(i32) -> () { - - some_closure(1); +where + F: FnMut(i32) -> (), +{ + some_closure(1); } fn main() { - let mut num = 5i32.nondet(); - let old_num = num; - { - let mut add_num = |x: i32| num += x; + let mut num = 5i32.verifier_nondet(); + let old_num = num; + { + let mut add_num = |x: i32| num += x; - add_num(5); - call_with_one(&mut add_num); - } - assert!(old_num + 6 != num); // Should be old_num + 6 + add_num(5); + call_with_one(&mut add_num); + } + smack::assert!(old_num + 6 != num); // Should be old_num + 6 } diff --git a/test/rust/functions/double.rs b/test/rust/functions/double.rs index 761afe1c9..414b940e0 100644 --- a/test/rust/functions/double.rs +++ b/test/rust/functions/double.rs @@ -1,15 +1,15 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect verified fn double(a: u32) -> u32 { - a * 2 + a * 2 } fn main() { - let a = 2u32.nondet(); - let b = double(a); - assert!(b == 2*a); + let a = 2u32.verifier_nondet(); + let b = double(a); + smack::assert!(b == 2 * a); } diff --git a/test/rust/functions/double_fail.rs b/test/rust/functions/double_fail.rs index 034b36db9..833cc8190 100644 --- a/test/rust/functions/double_fail.rs +++ b/test/rust/functions/double_fail.rs @@ -1,15 +1,15 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect error fn double(a: u32) -> u32 { - a * 2 + a * 2 } fn main() { - let a = 2u32.nondet(); - let b = double(a); - assert!(b != 2*a); + let a = 2u32.verifier_nondet(); + let b = double(a); + smack::assert!(b != 2 * a); } diff --git a/test/rust/generics/config.yml b/test/rust/generics/config.yml deleted file mode 100644 index f538f2a68..000000000 --- a/test/rust/generics/config.yml +++ /dev/null @@ -1 +0,0 @@ -memory: [no-reuse-impls, no-reuse] diff --git a/test/rust/generics/generic_function.rs b/test/rust/generics/generic_function.rs index 0cde8c545..adce0722a 100644 --- a/test/rust/generics/generic_function.rs +++ b/test/rust/generics/generic_function.rs @@ -1,54 +1,65 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect verified struct Point { - pub x:T, - pub y:T + pub x: T, + pub y: T, } struct Point3 { - pub x:T, - pub y:T, - pub z:T + pub x: T, + pub y: T, + pub z: T, } trait S { - fn swap_items(self)->Self; + fn swap_items(self) -> Self; } impl S for Point { - fn swap_items(self) -> Point { - Point::{x: self.y, y: self.x} - } + fn swap_items(self) -> Point { + Point:: { + x: self.y, + y: self.x, + } + } } impl S for Point3 { - fn swap_items(self) -> Point3 { - Point3::{x: self.y, y: self.z, z: self.x} - } + fn swap_items(self) -> Point3 { + Point3:: { + x: self.y, + y: self.z, + z: self.x, + } + } } fn swapem>(s: U) -> U { - s.swap_items() + s.swap_items() } fn main() { - let x2 = 7i64.nondet(); - let y2 = 8i64.nondet(); - let x3 = 1i64.nondet(); - let y3 = 2i64.nondet(); - let z3 = 3i64.nondet(); - let p2 = Point::{x: x2, y: y2}; - let p3 = Point3::{x: x3, y: y3, z: z3}; - - let q2 = swapem(p2); - let q3 = swapem(p3); - assert!(q2.x == y2); - assert!(q2.y == x2); - assert!(q3.x == y3); - assert!(q3.y == z3); - assert!(q3.z == x3); + let x2 = 7i64.verifier_nondet(); + let y2 = 8i64.verifier_nondet(); + let x3 = 1i64.verifier_nondet(); + let y3 = 2i64.verifier_nondet(); + let z3 = 3i64.verifier_nondet(); + let p2 = Point:: { x: x2, y: y2 }; + let p3 = Point3:: { + x: x3, + y: y3, + z: z3, + }; + + let q2 = swapem(p2); + let q3 = swapem(p3); + smack::assert!(q2.x == y2); + smack::assert!(q2.y == x2); + smack::assert!(q3.x == y3); + smack::assert!(q3.y == z3); + smack::assert!(q3.z == x3); } diff --git a/test/rust/generics/generic_function_fail1.rs b/test/rust/generics/generic_function_fail1.rs index 493e3b507..62d6736a2 100644 --- a/test/rust/generics/generic_function_fail1.rs +++ b/test/rust/generics/generic_function_fail1.rs @@ -1,54 +1,65 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect error struct Point { - pub x:T, - pub y:T + pub x: T, + pub y: T, } struct Point3 { - pub x:T, - pub y:T, - pub z:T + pub x: T, + pub y: T, + pub z: T, } trait S { - fn swap_items(self)->Self; + fn swap_items(self) -> Self; } impl S for Point { - fn swap_items(self) -> Point { - Point::{x: self.y, y: self.x} - } + fn swap_items(self) -> Point { + Point:: { + x: self.y, + y: self.x, + } + } } impl S for Point3 { - fn swap_items(self) -> Point3 { - Point3::{x: self.y, y: self.z, z: self.x} - } + fn swap_items(self) -> Point3 { + Point3:: { + x: self.y, + y: self.z, + z: self.x, + } + } } fn swapem>(s: U) -> U { - s.swap_items() + s.swap_items() } fn main() { - let x2 = 7i64.nondet(); - let y2 = 8i64.nondet(); - let x3 = 1i64.nondet(); - let y3 = 2i64.nondet(); - let z3 = 3i64.nondet(); - let p2 = Point::{x: x2, y: y2}; - let p3 = Point3::{x: x3, y: y3, z: z3}; - - let q2 = swapem(p2); - let q3 = swapem(p3); - assert!(q2.x != y2); - assert!(q2.y == x2); - assert!(q3.x == y3); - assert!(q3.y == z3); - assert!(q3.z == x3); + let x2 = 7i64.verifier_nondet(); + let y2 = 8i64.verifier_nondet(); + let x3 = 1i64.verifier_nondet(); + let y3 = 2i64.verifier_nondet(); + let z3 = 3i64.verifier_nondet(); + let p2 = Point:: { x: x2, y: y2 }; + let p3 = Point3:: { + x: x3, + y: y3, + z: z3, + }; + + let q2 = swapem(p2); + let q3 = swapem(p3); + smack::assert!(q2.x != y2); + smack::assert!(q2.y == x2); + smack::assert!(q3.x == y3); + smack::assert!(q3.y == z3); + smack::assert!(q3.z == x3); } diff --git a/test/rust/generics/generic_function_fail2.rs b/test/rust/generics/generic_function_fail2.rs index 9295c1d29..1a20199b1 100644 --- a/test/rust/generics/generic_function_fail2.rs +++ b/test/rust/generics/generic_function_fail2.rs @@ -1,54 +1,65 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect error struct Point { - pub x:T, - pub y:T + pub x: T, + pub y: T, } struct Point3 { - pub x:T, - pub y:T, - pub z:T + pub x: T, + pub y: T, + pub z: T, } trait S { - fn swap_items(self)->Self; + fn swap_items(self) -> Self; } impl S for Point { - fn swap_items(self) -> Point { - Point::{x: self.y, y: self.x} - } + fn swap_items(self) -> Point { + Point:: { + x: self.y, + y: self.x, + } + } } impl S for Point3 { - fn swap_items(self) -> Point3 { - Point3::{x: self.y, y: self.z, z: self.x} - } + fn swap_items(self) -> Point3 { + Point3:: { + x: self.y, + y: self.z, + z: self.x, + } + } } fn swapem>(s: U) -> U { - s.swap_items() + s.swap_items() } fn main() { - let x2 = 7i64.nondet(); - let y2 = 8i64.nondet(); - let x3 = 1i64.nondet(); - let y3 = 2i64.nondet(); - let z3 = 3i64.nondet(); - let p2 = Point::{x: x2, y: y2}; - let p3 = Point3::{x: x3, y: y3, z: z3}; - - let q2 = swapem(p2); - let q3 = swapem(p3); - assert!(q2.x == y2); - assert!(q2.y != x2); - assert!(q3.x == y3); - assert!(q3.y == z3); - assert!(q3.z == x3); + let x2 = 7i64.verifier_nondet(); + let y2 = 8i64.verifier_nondet(); + let x3 = 1i64.verifier_nondet(); + let y3 = 2i64.verifier_nondet(); + let z3 = 3i64.verifier_nondet(); + let p2 = Point:: { x: x2, y: y2 }; + let p3 = Point3:: { + x: x3, + y: y3, + z: z3, + }; + + let q2 = swapem(p2); + let q3 = swapem(p3); + smack::assert!(q2.x == y2); + smack::assert!(q2.y != x2); + smack::assert!(q3.x == y3); + smack::assert!(q3.y == z3); + smack::assert!(q3.z == x3); } diff --git a/test/rust/generics/generic_function_fail3.rs b/test/rust/generics/generic_function_fail3.rs index 9a800da1e..168de68e9 100644 --- a/test/rust/generics/generic_function_fail3.rs +++ b/test/rust/generics/generic_function_fail3.rs @@ -1,54 +1,65 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect error struct Point { - pub x:T, - pub y:T + pub x: T, + pub y: T, } struct Point3 { - pub x:T, - pub y:T, - pub z:T + pub x: T, + pub y: T, + pub z: T, } trait S { - fn swap_items(self)->Self; + fn swap_items(self) -> Self; } impl S for Point { - fn swap_items(self) -> Point { - Point::{x: self.y, y: self.x} - } + fn swap_items(self) -> Point { + Point:: { + x: self.y, + y: self.x, + } + } } impl S for Point3 { - fn swap_items(self) -> Point3 { - Point3::{x: self.y, y: self.z, z: self.x} - } + fn swap_items(self) -> Point3 { + Point3:: { + x: self.y, + y: self.z, + z: self.x, + } + } } fn swapem>(s: U) -> U { - s.swap_items() + s.swap_items() } fn main() { - let x2 = 7i64.nondet(); - let y2 = 8i64.nondet(); - let x3 = 1i64.nondet(); - let y3 = 2i64.nondet(); - let z3 = 3i64.nondet(); - let p2 = Point::{x: x2, y: y2}; - let p3 = Point3::{x: x3, y: y3, z: z3}; - - let q2 = swapem(p2); - let q3 = swapem(p3); - assert!(q2.x == y2); - assert!(q2.y == x2); - assert!(q3.x != y3); - assert!(q3.y == z3); - assert!(q3.z == x3); + let x2 = 7i64.verifier_nondet(); + let y2 = 8i64.verifier_nondet(); + let x3 = 1i64.verifier_nondet(); + let y3 = 2i64.verifier_nondet(); + let z3 = 3i64.verifier_nondet(); + let p2 = Point:: { x: x2, y: y2 }; + let p3 = Point3:: { + x: x3, + y: y3, + z: z3, + }; + + let q2 = swapem(p2); + let q3 = swapem(p3); + smack::assert!(q2.x == y2); + smack::assert!(q2.y == x2); + smack::assert!(q3.x != y3); + smack::assert!(q3.y == z3); + smack::assert!(q3.z == x3); } diff --git a/test/rust/generics/generic_function_fail4.rs b/test/rust/generics/generic_function_fail4.rs index 6aad57efd..104207d46 100644 --- a/test/rust/generics/generic_function_fail4.rs +++ b/test/rust/generics/generic_function_fail4.rs @@ -1,54 +1,65 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect error struct Point { - pub x:T, - pub y:T + pub x: T, + pub y: T, } struct Point3 { - pub x:T, - pub y:T, - pub z:T + pub x: T, + pub y: T, + pub z: T, } trait S { - fn swap_items(self)->Self; + fn swap_items(self) -> Self; } impl S for Point { - fn swap_items(self) -> Point { - Point::{x: self.y, y: self.x} - } + fn swap_items(self) -> Point { + Point:: { + x: self.y, + y: self.x, + } + } } impl S for Point3 { - fn swap_items(self) -> Point3 { - Point3::{x: self.y, y: self.z, z: self.x} - } + fn swap_items(self) -> Point3 { + Point3:: { + x: self.y, + y: self.z, + z: self.x, + } + } } fn swapem>(s: U) -> U { - s.swap_items() + s.swap_items() } fn main() { - let x2 = 7i64.nondet(); - let y2 = 8i64.nondet(); - let x3 = 1i64.nondet(); - let y3 = 2i64.nondet(); - let z3 = 3i64.nondet(); - let p2 = Point::{x: x2, y: y2}; - let p3 = Point3::{x: x3, y: y3, z: z3}; - - let q2 = swapem(p2); - let q3 = swapem(p3); - assert!(q2.x == y2); - assert!(q2.y == x2); - assert!(q3.x == y3); - assert!(q3.y != z3); - assert!(q3.z == x3); + let x2 = 7i64.verifier_nondet(); + let y2 = 8i64.verifier_nondet(); + let x3 = 1i64.verifier_nondet(); + let y3 = 2i64.verifier_nondet(); + let z3 = 3i64.verifier_nondet(); + let p2 = Point:: { x: x2, y: y2 }; + let p3 = Point3:: { + x: x3, + y: y3, + z: z3, + }; + + let q2 = swapem(p2); + let q3 = swapem(p3); + smack::assert!(q2.x == y2); + smack::assert!(q2.y == x2); + smack::assert!(q3.x == y3); + smack::assert!(q3.y != z3); + smack::assert!(q3.z == x3); } diff --git a/test/rust/generics/generic_function_fail5.rs b/test/rust/generics/generic_function_fail5.rs index 7c4ec4455..bad2e7677 100644 --- a/test/rust/generics/generic_function_fail5.rs +++ b/test/rust/generics/generic_function_fail5.rs @@ -1,54 +1,65 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect error struct Point { - pub x:T, - pub y:T + pub x: T, + pub y: T, } struct Point3 { - pub x:T, - pub y:T, - pub z:T + pub x: T, + pub y: T, + pub z: T, } trait S { - fn swap_items(self)->Self; + fn swap_items(self) -> Self; } impl S for Point { - fn swap_items(self) -> Point { - Point::{x: self.y, y: self.x} - } + fn swap_items(self) -> Point { + Point:: { + x: self.y, + y: self.x, + } + } } impl S for Point3 { - fn swap_items(self) -> Point3 { - Point3::{x: self.y, y: self.z, z: self.x} - } + fn swap_items(self) -> Point3 { + Point3:: { + x: self.y, + y: self.z, + z: self.x, + } + } } fn swapem>(s: U) -> U { - s.swap_items() + s.swap_items() } fn main() { - let x2 = 7i64.nondet(); - let y2 = 8i64.nondet(); - let x3 = 1i64.nondet(); - let y3 = 2i64.nondet(); - let z3 = 3i64.nondet(); - let p2 = Point::{x: x2, y: y2}; - let p3 = Point3::{x: x3, y: y3, z: z3}; - - let q2 = swapem(p2); - let q3 = swapem(p3); - assert!(q2.x == y2); - assert!(q2.y == x2); - assert!(q3.x == y3); - assert!(q3.y == z3); - assert!(q3.z != x3); + let x2 = 7i64.verifier_nondet(); + let y2 = 8i64.verifier_nondet(); + let x3 = 1i64.verifier_nondet(); + let y3 = 2i64.verifier_nondet(); + let z3 = 3i64.verifier_nondet(); + let p2 = Point:: { x: x2, y: y2 }; + let p3 = Point3:: { + x: x3, + y: y3, + z: z3, + }; + + let q2 = swapem(p2); + let q3 = swapem(p3); + smack::assert!(q2.x == y2); + smack::assert!(q2.y == x2); + smack::assert!(q3.x == y3); + smack::assert!(q3.y == z3); + smack::assert!(q3.z != x3); } diff --git a/test/rust/loops/gauss_sum_nondet.rs b/test/rust/loops/gauss_sum_nondet.rs deleted file mode 100644 index fee9cf404..000000000 --- a/test/rust/loops/gauss_sum_nondet.rs +++ /dev/null @@ -1,16 +0,0 @@ -#[macro_use] -mod smack; -use smack::*; - -// @flag --unroll=4 -// @expect verified - -fn main() { - let mut sum = 0; - let b = 7u64.nondet(); - assume!(b < 5 && b > 1); - for i in 0..b as u64 { - sum += i; - } - assert!(2*sum == b*(b-1)); -} diff --git a/test/rust/loops/gauss_sum_nondet_fail.rs b/test/rust/loops/gauss_sum_nondet_fail.rs deleted file mode 100644 index 47c79428a..000000000 --- a/test/rust/loops/gauss_sum_nondet_fail.rs +++ /dev/null @@ -1,16 +0,0 @@ -#[macro_use] -mod smack; -use smack::*; - -// @flag --unroll=10 -// @expect error - -fn main() { - let mut sum = 0; - let b = 7u64.nondet(); - assume!(b > 1); - for i in 0..b as u64 { - sum += i; - } - assert!(2*sum != b*(b-1)); -} diff --git a/test/rust/loops/iterator.rs b/test/rust/loops/iterator.rs index 20dad0d14..3a012be89 100644 --- a/test/rust/loops/iterator.rs +++ b/test/rust/loops/iterator.rs @@ -1,24 +1,24 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @flag --unroll=4 // @expect verified fn fac(n: u64) -> u64 { - match n { - 0 => 1, - 1 => 1, - _ => n*fac(n-1) - } + match n { + 0 => 1, + 1 => 1, + _ => n * fac(n - 1), + } } fn main() { - let mut a = 1; - let n = 6u64.nondet(); - assume!(n < 5); - for i in 1..n+1 as u64 { - a *= i; - } - assert!(a == fac(n)); // a == 6! + let mut a = 1; + let n = 6u64.verifier_nondet(); + smack::assume!(n < 5); + for i in 1..n + 1 as u64 { + a *= i; + } + smack::assert!(a == fac(n)); // a == 6! } diff --git a/test/rust/loops/iterator_fail.rs b/test/rust/loops/iterator_fail.rs index 2f740e534..c64648cba 100644 --- a/test/rust/loops/iterator_fail.rs +++ b/test/rust/loops/iterator_fail.rs @@ -1,22 +1,22 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @flag --unroll=10 // @expect error fn fac(n: u64) -> u64 { - match n { - 0 => 1, - 1 => 1, - _ => n*fac(n-1) - } + match n { + 0 => 1, + 1 => 1, + _ => n * fac(n - 1), + } } fn main() { - let mut a = 1; - let n = 6u64.nondet(); - for i in 1..n+1 as u64 { - a *= i; - } - assert!(a != fac(n)); // a should equal 6! + let mut a = 1; + let n = 6u64.verifier_nondet(); + for i in 1..n + 1 as u64 { + a *= i; + } + smack::assert!(a != fac(n)); // a should equal 6! } diff --git a/test/rust/panic/config.yml b/test/rust/panic/config.yml new file mode 100644 index 000000000..bb0984674 --- /dev/null +++ b/test/rust/panic/config.yml @@ -0,0 +1 @@ +verifiers: [corral] diff --git a/test/rust/panic/core_panic_fail.rs b/test/rust/panic/core_panic_fail.rs new file mode 100644 index 000000000..7ac49355e --- /dev/null +++ b/test/rust/panic/core_panic_fail.rs @@ -0,0 +1,23 @@ +#![no_std] +#![feature(lang_items)] +#![feature(start)] +use core::panic::PanicInfo; + +// @flag --check=rust-panics +// @expect error +// @checkout grep "SMACK found an error: Rust panic." + +#[start] +fn main(_x: isize, _y: *const *const u8) -> isize { + panic!(); +} + +#[panic_handler] +fn panic(_expr: &PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +fn eh() { + +} diff --git a/test/rust/panic/core_panic_fmt_fail.rs b/test/rust/panic/core_panic_fmt_fail.rs new file mode 100644 index 000000000..142fd8215 --- /dev/null +++ b/test/rust/panic/core_panic_fmt_fail.rs @@ -0,0 +1,23 @@ +#![no_std] +#![feature(lang_items)] +#![feature(start)] +use core::panic::PanicInfo; + +// @flag --check=rust-panics +// @expect error +// @checkout grep "SMACK found an error: Rust panic." + +#[start] +fn main(_x: isize, _y: *const *const u8) -> isize { + panic!("Something {}", 7); +} + +#[panic_handler] +fn panic(_expr: &PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +fn eh() { + +} diff --git a/test/rust/panic/std_panic.rs b/test/rust/panic/std_panic.rs new file mode 100644 index 000000000..c705a75b8 --- /dev/null +++ b/test/rust/panic/std_panic.rs @@ -0,0 +1,6 @@ +// @expect verified +// This is run without checking for panics. + +fn main() { + panic!(); +} diff --git a/test/rust/panic/std_panic_fail.rs b/test/rust/panic/std_panic_fail.rs new file mode 100644 index 000000000..f55944c9f --- /dev/null +++ b/test/rust/panic/std_panic_fail.rs @@ -0,0 +1,7 @@ +// @flag --check=rust-panics +// @expect error +// @checkout grep "SMACK found an error: Rust panic." + +fn main() { + panic!(); +} diff --git a/test/rust/panic/std_panic_fmt_fail.rs b/test/rust/panic/std_panic_fmt_fail.rs new file mode 100644 index 000000000..76172ed50 --- /dev/null +++ b/test/rust/panic/std_panic_fmt_fail.rs @@ -0,0 +1,7 @@ +// @flag --check=rust-panics +// @expect error +// @checkout grep "SMACK found an error: Rust panic." + +fn main() { + panic!("{}", 7); +} diff --git a/test/rust/recursion/config.yml b/test/rust/recursion/config.yml index 03dbc1e0f..3a504bd1b 100644 --- a/test/rust/recursion/config.yml +++ b/test/rust/recursion/config.yml @@ -1 +1 @@ -verifiers: [corral] \ No newline at end of file +flags: [--unroll=6] diff --git a/test/rust/recursion/fac.rs b/test/rust/recursion/fac.rs index cbbebc0dd..c55b77f67 100644 --- a/test/rust/recursion/fac.rs +++ b/test/rust/recursion/fac.rs @@ -1,18 +1,17 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; -// @flag --unroll=10 // @expect verified fn fac(n: u64, acc: u64) -> u64 { - match n { - 0 => acc, - _ => fac(n - 1, acc * n) - } + match n { + 0 => acc, + _ => fac(n - 1, acc * n), + } } fn main() { - let x = fac(5, 1); - assert!(x == 120); + let x = fac(5, 1); + smack::assert!(x == 120); } diff --git a/test/rust/recursion/fac_fail.rs b/test/rust/recursion/fac_fail.rs index c8d9c8259..0f736a9bc 100644 --- a/test/rust/recursion/fac_fail.rs +++ b/test/rust/recursion/fac_fail.rs @@ -1,18 +1,17 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; -// @flag --unroll=10 // @expect error fn fac(n: u64, acc: u64) -> u64 { - match n { - 0 => acc, - _ => fac(n - 1, acc * n) - } + match n { + 0 => acc, + _ => fac(n - 1, acc * n), + } } fn main() { - let x = fac(5, 1); - assert!(x != 120); + let x = fac(5, 1); + smack::assert!(x != 120); } diff --git a/test/rust/recursion/fib.rs b/test/rust/recursion/fib.rs index a03027636..15ef74b4d 100644 --- a/test/rust/recursion/fib.rs +++ b/test/rust/recursion/fib.rs @@ -1,19 +1,18 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; -// @flag --unroll=10 // @expect verified fn fib(x: u64) -> u64 { - match x { - 0 => 1, - 1 => 1, - _ => fib(x-1) + fib(x-2) - } + match x { + 0 => 1, + 1 => 1, + _ => fib(x - 1) + fib(x - 2), + } } fn main() { - let x = fib(6); - assert!(x == 13); + let x = fib(6); + smack::assert!(x == 13); } diff --git a/test/rust/recursion/fib_fail.rs b/test/rust/recursion/fib_fail.rs index 9b6dee652..816845f1f 100644 --- a/test/rust/recursion/fib_fail.rs +++ b/test/rust/recursion/fib_fail.rs @@ -1,19 +1,18 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; -// @flag --unroll=10 // @expect error fn fib(x: u64) -> u64 { - match x { - 0 => 1, - 1 => 1, - _ => fib(x-1) + fib(x-2) - } + match x { + 0 => 1, + 1 => 1, + _ => fib(x - 1) + fib(x - 2), + } } fn main() { - let x = fib(6); - assert!(x != 13); + let x = fib(6); + smack::assert!(x != 13); } diff --git a/test/rust/structures/enum_basic.rs b/test/rust/structures/enum_basic.rs new file mode 100644 index 000000000..00ccbb95b --- /dev/null +++ b/test/rust/structures/enum_basic.rs @@ -0,0 +1,24 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +enum Heist { + GetAway, + LeaveWitnesses(u8), +} + +fn main() { + let w = 1u8.verifier_nondet(); + smack::assume!(w == 0); + let h = if w == 0 { + Heist::GetAway + } else { + Heist::LeaveWitnesses(w) + }; + match h { + Heist::GetAway => (), + Heist::LeaveWitnesses(_) => smack::assert!(0), + }; +} diff --git a/test/rust/structures/enum_basic_fail.rs b/test/rust/structures/enum_basic_fail.rs new file mode 100644 index 000000000..0c07174dc --- /dev/null +++ b/test/rust/structures/enum_basic_fail.rs @@ -0,0 +1,23 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +enum Heist { + GetAway, + LeaveWitnesses(u8), +} + +fn main() { + let w = 1u8.verifier_nondet(); + let h = if w == 0 { + Heist::GetAway + } else { + Heist::LeaveWitnesses(w) + }; + match h { + Heist::GetAway => (), + Heist::LeaveWitnesses(_) => smack::assert!(0), + }; +} diff --git a/test/rust/structures/nested_struct.rs b/test/rust/structures/nested_struct.rs new file mode 100644 index 000000000..a33d30730 --- /dev/null +++ b/test/rust/structures/nested_struct.rs @@ -0,0 +1,33 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +struct Point { + x: i32, + y: i32, +} + +struct Pair { + x: Point, + y: Point, +} + +fn valid(p: &Pair) -> bool { + p.x.x != p.y.x || p.x.y != p.y.y +} + +fn main() { + let x = Point { + x: 1i32.verifier_nondet(), + y: 2i32.verifier_nondet(), + }; + let y = Point { + x: 2i32.verifier_nondet(), + y: 3i32.verifier_nondet(), + }; + smack::assume!(x.x != y.x); + let p = Pair { x: x, y: y }; + smack::assert!(valid(&p)); +} diff --git a/test/rust/structures/nested_struct_assign.rs b/test/rust/structures/nested_struct_assign.rs new file mode 100644 index 000000000..65ee3744f --- /dev/null +++ b/test/rust/structures/nested_struct_assign.rs @@ -0,0 +1,43 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +struct Point { + x: i32, + y: i32, +} + +struct Pair { + x: Point, + y: Point, +} + +fn valid(p: &Pair) -> bool { + p.x.x != p.y.x || p.x.y != p.y.y +} + +fn main() { + let x = Point { + x: 1i32.verifier_nondet(), + y: 2i32.verifier_nondet(), + }; + let y = Point { + x: 2i32.verifier_nondet(), + y: 3i32.verifier_nondet(), + }; + let m = Point { + x: 1i32.verifier_nondet(), + y: 2i32.verifier_nondet(), + }; + let n = Point { + x: 2i32.verifier_nondet(), + y: 3i32.verifier_nondet(), + }; + smack::assume!(n.x != y.x); + let mut p = Pair { x: x, y: y }; + let q = Pair { x: m, y: n }; + p.x = q.y; + smack::assert!(valid(&p)); +} diff --git a/test/rust/structures/nested_struct_assign_fail.rs b/test/rust/structures/nested_struct_assign_fail.rs new file mode 100644 index 000000000..6961d6007 --- /dev/null +++ b/test/rust/structures/nested_struct_assign_fail.rs @@ -0,0 +1,43 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +struct Point { + x: i32, + y: i32, +} + +struct Pair { + x: Point, + y: Point, +} + +fn valid(p: &Pair) -> bool { + p.x.x != p.y.x || p.x.y != p.y.y +} + +fn main() { + let x = Point { + x: 1i32.verifier_nondet(), + y: 2i32.verifier_nondet(), + }; + let y = Point { + x: 2i32.verifier_nondet(), + y: 3i32.verifier_nondet(), + }; + let m = Point { + x: 1i32.verifier_nondet(), + y: 2i32.verifier_nondet(), + }; + let n = Point { + x: 2i32.verifier_nondet(), + y: 3i32.verifier_nondet(), + }; + smack::assume!(n.x != y.x); + let mut p = Pair { x: x, y: y }; + let q = Pair { x: m, y: n }; + p.x = q.y; + smack::assert!(!valid(&p)); +} diff --git a/test/rust/structures/nested_struct_fail.rs b/test/rust/structures/nested_struct_fail.rs new file mode 100644 index 000000000..3744a878b --- /dev/null +++ b/test/rust/structures/nested_struct_fail.rs @@ -0,0 +1,32 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +struct Point { + x: i32, + y: i32, +} + +struct Pair { + x: Point, + y: Point, +} + +fn valid(p: &Pair) -> bool { + p.x.x != p.y.x || p.x.y != p.y.y +} + +fn main() { + let x = Point { + x: 1i32.verifier_nondet(), + y: 2i32.verifier_nondet(), + }; + let y = Point { + x: 2i32.verifier_nondet(), + y: 3i32.verifier_nondet(), + }; + let p = Pair { x: x, y: y }; + smack::assert!(valid(&p)); +} diff --git a/test/rust/structures/nested_struct_ref.rs b/test/rust/structures/nested_struct_ref.rs new file mode 100644 index 000000000..1f28d141c --- /dev/null +++ b/test/rust/structures/nested_struct_ref.rs @@ -0,0 +1,33 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +struct Point { + x: i32, + y: i32, +} + +struct Pair<'a> { + x: &'a Point, + y: &'a Point, +} + +fn valid(p: &Pair) -> bool { + p.x.x != p.y.x || p.x.y != p.y.y +} + +fn main() { + let x = Point { + x: 1i32.verifier_nondet(), + y: 2i32.verifier_nondet(), + }; + let y = Point { + x: 2i32.verifier_nondet(), + y: 3i32.verifier_nondet(), + }; + smack::assume!(x.x != y.x); + let p = Pair { x: &x, y: &y }; + smack::assert!(valid(&p)); +} diff --git a/test/rust/structures/nested_struct_ref_fail.rs b/test/rust/structures/nested_struct_ref_fail.rs new file mode 100644 index 000000000..6487982c2 --- /dev/null +++ b/test/rust/structures/nested_struct_ref_fail.rs @@ -0,0 +1,33 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +struct Point { + x: i32, + y: i32, +} + +struct Pair<'a> { + x: &'a Point, + y: &'a Point, +} + +fn valid(p: &Pair) -> bool { + p.x.x != p.y.x || p.x.y != p.y.y +} + +fn main() { + let x = Point { + x: 1i32.verifier_nondet(), + y: 2i32.verifier_nondet(), + }; + let y = Point { + x: 2i32.verifier_nondet(), + y: 3i32.verifier_nondet(), + }; + smack::assume!(x.x != y.x); + let p = Pair { x: &x, y: &y }; + smack::assert!(!valid(&p)); +} diff --git a/test/rust/structures/option.rs b/test/rust/structures/option.rs deleted file mode 100644 index cbfb1e52a..000000000 --- a/test/rust/structures/option.rs +++ /dev/null @@ -1,29 +0,0 @@ -#[macro_use] -mod smack; -use smack::*; - -// @expect verified - -fn safe_div(x: u64, y: u64) -> Option { - if y != 0 { - Some(x/y) - } - else { - None - } -} - -fn main() { - let x = 2u64.nondet(); - assume!(x > 0); - let a = safe_div(2*x,x); - match a { - Some(x) => assert!(x == 2), - None => assert!(false) - }; - let b = safe_div(x,0); - match b { - Some(x) => assert!(false), - None => assert!(true) - }; -} diff --git a/test/rust/structures/option_basic.rs b/test/rust/structures/option_basic.rs new file mode 100644 index 000000000..99087e02c --- /dev/null +++ b/test/rust/structures/option_basic.rs @@ -0,0 +1,23 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +fn checked_addition(l: u8, r: u8) -> Option { + let s = l + r; + if s > 254 { + None + } else { + Some(s) + } +} + +fn main() { + let a = 1u8.verifier_nondet(); + let b = 2u8.verifier_nondet(); + smack::assume!(a < 128); + smack::assume!(b < 127); + let r = checked_addition(a, b); + smack::assert!(r.is_some()); +} diff --git a/test/rust/structures/option_basic_fail.rs b/test/rust/structures/option_basic_fail.rs new file mode 100644 index 000000000..b78ddd28e --- /dev/null +++ b/test/rust/structures/option_basic_fail.rs @@ -0,0 +1,23 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +fn checked_addition(l: u8, r: u8) -> Option { + let s = l + r; + if s > 254 { + None + } else { + Some(s) + } +} + +fn main() { + let a = 1u8.verifier_nondet(); + let b = 2u8.verifier_nondet(); + smack::assume!(a < 128); + smack::assume!(b < 127); + let r = checked_addition(a, b); + smack::assert!(r.is_none()); +} diff --git a/test/rust/structures/option_fail.rs b/test/rust/structures/option_fail.rs deleted file mode 100644 index dc11d8f5b..000000000 --- a/test/rust/structures/option_fail.rs +++ /dev/null @@ -1,29 +0,0 @@ -#[macro_use] -mod smack; -use smack::*; - -// @expect error - -fn safe_div(x: u64, y: u64) -> Option { - if y != 0 { - Some(x/y) - } - else { - None - } -} - -fn main() { - let x = 2u64.nondet(); - assume!(x > 0); - let a = safe_div(2*x,x); - match a { - Some(x) => assert!(x == 2), - None => assert!(false) - }; - let b = safe_div(x,0); - match b { - Some(x) => assert!(true), - None => assert!(false) // Division by zero should return None - }; -} diff --git a/test/rust/structures/phantom_data.rs b/test/rust/structures/phantom_data.rs new file mode 100644 index 000000000..9ed30ffe0 --- /dev/null +++ b/test/rust/structures/phantom_data.rs @@ -0,0 +1,29 @@ +use std::marker::PhantomData; + +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +struct X { + x: i32, + phantom: PhantomData, // To consume the generic type: this is zero-sized (type {}) +} + +struct S { + pub phantom: X, + pub data: i32, +} + +fn main() { + let mut x = S:: { + phantom: X { + x: 7, + phantom: PhantomData, + }, + data: 4, + }; + x.data += 1; + smack::assert!(x.data == 5); +} diff --git a/test/rust/structures/phantom_data_fail.rs b/test/rust/structures/phantom_data_fail.rs new file mode 100644 index 000000000..7d8ee946d --- /dev/null +++ b/test/rust/structures/phantom_data_fail.rs @@ -0,0 +1,29 @@ +use std::marker::PhantomData; + +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +struct X { + x: i32, + phantom: PhantomData, // To consume the generic type: this is zero-sized (type {}) +} + +struct S { + pub phantom: X, + pub data: i32, +} + +fn main() { + let mut x = S:: { + phantom: X { + x: 7, + phantom: PhantomData, + }, + data: 4, + }; + x.data += 1; + smack::assert!(x.data == 6); +} diff --git a/test/rust/structures/point.rs b/test/rust/structures/point.rs index 34caa22ce..b986c81b4 100644 --- a/test/rust/structures/point.rs +++ b/test/rust/structures/point.rs @@ -1,51 +1,51 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect verified use std::ops::{Add, AddAssign}; -#[derive(PartialEq,Clone,Copy)] +#[derive(PartialEq, Clone, Copy)] struct Point { - x: u64, - y: u64 + x: u64, + y: u64, } impl Point { - pub fn new(_x: u64, _y: u64) -> Point { - Point { x: _x, y: _y } - } - pub fn get_x(self) -> u64 { - self.x - } - pub fn get_y(self) -> u64 { - self.y - } + pub fn new(_x: u64, _y: u64) -> Point { + Point { x: _x, y: _y } + } + pub fn get_x(self) -> u64 { + self.x + } + pub fn get_y(self) -> u64 { + self.y + } } impl Add for Point { - type Output = Point; - fn add(self, other: Point) -> Point { - Point::new(self.x + other.x, self.y + other.y) - } + type Output = Point; + fn add(self, other: Point) -> Point { + Point::new(self.x + other.x, self.y + other.y) + } } impl AddAssign for Point { - fn add_assign(&mut self, other: Point) { - self.x += other.x; - self.y += other.y; - } + fn add_assign(&mut self, other: Point) { + self.x += other.x; + self.y += other.y; + } } fn main() { - let w = 1u64.nondet(); - let x = 2u64.nondet(); - let y = 3u64.nondet(); - let z = 4u64.nondet(); - - let a = Point::new(w, x); - let b = Point::new(y, z); - let c = a + b; - assert!(c == Point::new(w+y,x+z)); + let w = 1u64.verifier_nondet(); + let x = 2u64.verifier_nondet(); + let y = 3u64.verifier_nondet(); + let z = 4u64.verifier_nondet(); + + let a = Point::new(w, x); + let b = Point::new(y, z); + let c = a + b; + smack::assert!(c == Point::new(w + y, x + z)); } diff --git a/test/rust/structures/point_as_tuple.rs b/test/rust/structures/point_as_tuple.rs new file mode 100644 index 000000000..77c7c1b5b --- /dev/null +++ b/test/rust/structures/point_as_tuple.rs @@ -0,0 +1,15 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +struct Point(u64, u64); + +fn main() { + let x = Point(1u64.verifier_nondet(), 2u64.verifier_nondet()); + let y = Point(3u64.verifier_nondet(), 4u64.verifier_nondet()); + let z = Point(x.0 + y.0, x.1 + y.1); + let Point(p, q) = z; + smack::assert!(p >= x.0 && q >= y.1); +} diff --git a/test/rust/structures/point_as_tuple_fail.rs b/test/rust/structures/point_as_tuple_fail.rs new file mode 100644 index 000000000..822c72399 --- /dev/null +++ b/test/rust/structures/point_as_tuple_fail.rs @@ -0,0 +1,15 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +struct Point(u64, u64); + +fn main() { + let x = Point(1u64.verifier_nondet(), 2u64.verifier_nondet()); + let y = Point(3u64.verifier_nondet(), 4u64.verifier_nondet()); + let z = Point(x.0 + y.0, x.1 + y.1); + let Point(p, q) = z; + smack::assert!(p < x.0 || q < y.1); +} diff --git a/test/rust/structures/point_fail.rs b/test/rust/structures/point_fail.rs index 9a228ce66..408ca1c48 100644 --- a/test/rust/structures/point_fail.rs +++ b/test/rust/structures/point_fail.rs @@ -1,51 +1,51 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect error use std::ops::{Add, AddAssign}; -#[derive(PartialEq,Clone,Copy)] +#[derive(PartialEq, Clone, Copy)] struct Point { - x: u64, - y: u64 + x: u64, + y: u64, } impl Point { - pub fn new(_x: u64, _y: u64) -> Point { - Point { x: _x, y: _y } - } - pub fn get_x(self) -> u64 { - self.x - } - pub fn get_y(self) -> u64 { - self.y - } + pub fn new(_x: u64, _y: u64) -> Point { + Point { x: _x, y: _y } + } + pub fn get_x(self) -> u64 { + self.x + } + pub fn get_y(self) -> u64 { + self.y + } } impl Add for Point { - type Output = Point; - fn add(self, other: Point) -> Point { - Point::new(self.x + other.x, self.y + other.y) - } + type Output = Point; + fn add(self, other: Point) -> Point { + Point::new(self.x + other.x, self.y + other.y) + } } impl AddAssign for Point { - fn add_assign(&mut self, other: Point) { - self.x += other.x; - self.y += other.y; - } + fn add_assign(&mut self, other: Point) { + self.x += other.x; + self.y += other.y; + } } fn main() { - let w = 1u64.nondet(); - let x = 2u64.nondet(); - let y = 3u64.nondet(); - let z = 4u64.nondet(); - - let a = Point::new(w,x); - let b = Point::new(y,z); - let c = a + b; - assert!(c != Point::new(w+y,x+z)); + let w = 1u64.verifier_nondet(); + let x = 2u64.verifier_nondet(); + let y = 3u64.verifier_nondet(); + let z = 4u64.verifier_nondet(); + + let a = Point::new(w, x); + let b = Point::new(y, z); + let c = a + b; + smack::assert!(c != Point::new(w + y, x + z)); } diff --git a/test/rust/structures/result_basic.rs b/test/rust/structures/result_basic.rs new file mode 100644 index 000000000..ffbfbd19f --- /dev/null +++ b/test/rust/structures/result_basic.rs @@ -0,0 +1,25 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect verified + +struct NaN; + +fn checked_addition(l: u8, r: u8) -> Result { + let s = l + r; + if s > 254 { + Err(NaN) + } else { + Ok(s) + } +} + +fn main() { + let a = 1u8.verifier_nondet(); + let b = 2u8.verifier_nondet(); + smack::assume!(a < 128); + smack::assume!(b < 127); + let r = checked_addition(a, b); + smack::assert!(r.is_ok() && r.unwrap_or(255) < 255); +} diff --git a/test/rust/structures/result_basic_fail.rs b/test/rust/structures/result_basic_fail.rs new file mode 100644 index 000000000..00432ba14 --- /dev/null +++ b/test/rust/structures/result_basic_fail.rs @@ -0,0 +1,25 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +struct NaN; + +fn checked_addition(l: u8, r: u8) -> Result { + let s = l + r; + if s > 254 { + Err(NaN) + } else { + Ok(s) + } +} + +fn main() { + let a = 1u8.verifier_nondet(); + let b = 2u8.verifier_nondet(); + smack::assume!(a <= 128); + smack::assume!(b < 128); + let r = checked_addition(a, b); + smack::assert!(r.is_ok() && r.unwrap_or(255) < 255); +} diff --git a/test/rust/vector/config.yml b/test/rust/vector/config.yml deleted file mode 100644 index f538f2a68..000000000 --- a/test/rust/vector/config.yml +++ /dev/null @@ -1 +0,0 @@ -memory: [no-reuse-impls, no-reuse] diff --git a/test/rust/vector/vec1.rs b/test/rust/vector/vec1.rs index eca63922b..2f7fb86db 100644 --- a/test/rust/vector/vec1.rs +++ b/test/rust/vector/vec1.rs @@ -1,19 +1,19 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect verified fn main() { - let mut v: Vec = Vec::new(); - v.push(0); - v.push(1); - v.push(3); - assert!(v[0] == 0); - assert!(v[1] == 1); - assert!(v[2] == 3); - v[2] = v[0]+v[1]; - assert!(v[0] == 0); - assert!(v[1] == 1); - assert!(v[2] == 1); + let mut v: Vec = Vec::new(); + v.push(0); + v.push(1); + v.push(3); + smack::assert!(v[0] == 0); + smack::assert!(v[1] == 1); + smack::assert!(v[2] == 3); + v[2] = v[0] + v[1]; + smack::assert!(v[0] == 0); + smack::assert!(v[1] == 1); + smack::assert!(v[2] == 1); } diff --git a/test/rust/vector/vec1_fail1.rs b/test/rust/vector/vec1_fail1.rs index de2d5fdd1..470a31882 100644 --- a/test/rust/vector/vec1_fail1.rs +++ b/test/rust/vector/vec1_fail1.rs @@ -1,19 +1,19 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect error fn main() { - let mut v: Vec = Vec::new(); - v.push(0); - v.push(1); - v.push(3); - assert!(v[0] == 0); - assert!(v[1] == 1); - assert!(v[2] == 3); - v[2] = v[0]+v[1]; - assert!(v[0] != 0); - assert!(v[1] == 1); - assert!(v[2] == 1); + let mut v: Vec = Vec::new(); + v.push(0); + v.push(1); + v.push(3); + smack::assert!(v[0] == 0); + smack::assert!(v[1] == 1); + smack::assert!(v[2] == 3); + v[2] = v[0] + v[1]; + smack::assert!(v[0] != 0); + smack::assert!(v[1] == 1); + smack::assert!(v[2] == 1); } diff --git a/test/rust/vector/vec1_fail2.rs b/test/rust/vector/vec1_fail2.rs index 79e642f58..eaad93437 100644 --- a/test/rust/vector/vec1_fail2.rs +++ b/test/rust/vector/vec1_fail2.rs @@ -1,19 +1,19 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect error fn main() { - let mut v: Vec = Vec::new(); - v.push(0); - v.push(1); - v.push(3); - assert!(v[0] == 0); - assert!(v[1] == 1); - assert!(v[2] == 3); - v[2] = v[0]+v[1]; - assert!(v[0] == 0); - assert!(v[1] != 1); - assert!(v[2] == 1); + let mut v: Vec = Vec::new(); + v.push(0); + v.push(1); + v.push(3); + smack::assert!(v[0] == 0); + smack::assert!(v[1] == 1); + smack::assert!(v[2] == 3); + v[2] = v[0] + v[1]; + smack::assert!(v[0] == 0); + smack::assert!(v[1] != 1); + smack::assert!(v[2] == 1); } diff --git a/test/rust/vector/vec1_fail3.rs b/test/rust/vector/vec1_fail3.rs index dbf298842..9814adf05 100644 --- a/test/rust/vector/vec1_fail3.rs +++ b/test/rust/vector/vec1_fail3.rs @@ -1,19 +1,19 @@ #[macro_use] -mod smack; +extern crate smack; use smack::*; // @expect error fn main() { - let mut v: Vec = Vec::new(); - v.push(0); - v.push(1); - v.push(3); - assert!(v[0] == 0); - assert!(v[1] == 1); - assert!(v[2] == 3); - v[2] = v[0]+v[1]; - assert!(v[0] == 0); - assert!(v[1] == 1); - assert!(v[2] != 1); + let mut v: Vec = Vec::new(); + v.push(0); + v.push(1); + v.push(3); + smack::assert!(v[0] == 0); + smack::assert!(v[1] == 1); + smack::assert!(v[2] == 3); + v[2] = v[0] + v[1]; + smack::assert!(v[0] == 0); + smack::assert!(v[1] == 1); + smack::assert!(v[2] != 1); } diff --git a/test/rust/vector/vec2_fail.rs b/test/rust/vector/vec2_fail.rs new file mode 100644 index 000000000..31178111d --- /dev/null +++ b/test/rust/vector/vec2_fail.rs @@ -0,0 +1,13 @@ +#[macro_use] +extern crate smack; +use smack::*; + +// @expect error + +fn main() { + let mut x: Vec = Vec::new(); + let mut y: Vec = Vec::new(); + x.push(0); + y.push(0); + smack::assert!(x[0] != y[0]); +} diff --git a/test/rust/vector/vec_resize.rs b/test/rust/vector/vec_resize.rs deleted file mode 100644 index c6a5ccc22..000000000 --- a/test/rust/vector/vec_resize.rs +++ /dev/null @@ -1,13 +0,0 @@ -#[macro_use] -mod smack; -use smack::*; - -// @flag --unroll=3 -// @expect verified - -fn main() { - let mut v1:Vec = vec![0]; - let mut v2:Vec = vec![3]; - v1.append(&mut v2); - assert!(v1[1] == 3); -} diff --git a/test/rust/vector/vec_resize_fail.rs b/test/rust/vector/vec_resize_fail.rs deleted file mode 100644 index 7dad2eadd..000000000 --- a/test/rust/vector/vec_resize_fail.rs +++ /dev/null @@ -1,13 +0,0 @@ -#[macro_use] -mod smack; -use smack::*; - -// @flag --unroll=3 -// @expect error - -fn main() { - let mut v1:Vec = vec![0]; - let mut v2:Vec = vec![3]; - v1.append(&mut v2); - assert!(v1[1] != 3); -} diff --git a/tools/llvm2bpl/llvm2bpl.cpp b/tools/llvm2bpl/llvm2bpl.cpp index f16935d41..0a71fdf65 100644 --- a/tools/llvm2bpl/llvm2bpl.cpp +++ b/tools/llvm2bpl/llvm2bpl.cpp @@ -32,6 +32,7 @@ #include "smack/BplFilePrinter.h" #include "smack/CodifyStaticInits.h" #include "smack/ExtractContracts.h" +#include "smack/InitializePasses.h" #include "smack/IntegerOverflowChecker.h" #include "smack/MemorySafetyChecker.h" #include "smack/NormalizeLoops.h" @@ -116,10 +117,6 @@ int main(int argc, char **argv) { llvm::cl::ParseCommandLineOptions( argc, argv, "llvm2bpl - LLVM bitcode to Boogie transformation\n"); - if (smack::SmackOptions::BitPrecisePointers) { - smack::SmackOptions::BitPrecise = true; - } - llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); llvm::PrettyStackTraceProgram PSTP(argc, argv); llvm::EnableDebugBuffering = true; @@ -148,6 +145,8 @@ int main(int argc, char **argv) { llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry(); llvm::initializeAnalysis(Registry); + llvm::initializeCodifyStaticInitsPass(Registry); + llvm::legacy::PassManager pass_manager; pass_manager.add(llvm::createLowerSwitchPass()); @@ -170,7 +169,7 @@ int main(int argc, char **argv) { pass_manager.add(new smack::ExtractContracts()); pass_manager.add(new smack::VerifierCodeMetadata()); pass_manager.add(llvm::createDeadCodeEliminationPass()); - pass_manager.add(new smack::CodifyStaticInits()); + pass_manager.add(smack::createCodifyStaticInitsPass()); if (!Modular) { pass_manager.add(new smack::RemoveDeadDefs()); }