How Tipi.Work Runs A Coverity Scan Build In A Container On FreeBSD

Coverity Scan is a free, cloud-based static analysis service provided by Synopsys for the Open Source community. It is a powerful tool for finding defects and security vulnerabilities in code before they hit production.

The core of using Coverity is the build capture process, where the Coverity tool intercepts your standard compiler calls (like gcc, clang, cl or javac) to create an intermediate cov-int directory containing a detailed model of your codebase.

Here is a step-by-step guide on how to perform a Coverity Scan build, focusing on the standard command-line workflow. Important note here is we're going to use Ubuntu Linux container to run Coverity Scan software since it's unavailable for FreeBSD.

Phase 1: Setup and Download

Before you begin, your project must be registered with the Coverity Scan service (for Open Source projects).

  1. Project Registration: Register your Open Source project on the Coverity Scan website to obtain a Project Token and a corresponding stream name. So, I registered my project
  2. Download the Toolset: Download the Coverity Analysis installer (often referred to as the Coverity CLI or Thin Client) from the Coverity Scan dashboard. The Coverity Scan software is unavailable for FreeBSD, so I downloaded a latest version for Linux, cov-analysis-linux64-2024.12.1.tar.gz.
  3. Set Up the Environment: Extract the downloaded archive and add the /bin directory of the Coverity installation to your system's PATH. This allows you to run commands like cov-build directly.
  4. It's closer to my case. But at first I installed sysutils/podman-suite. Enabled corresponding service in /etc/rc.conf and start them:
        
    podman_enable="YES"
    podman_service_enable="YES"
    podman_service_flags="--log-level=debug --time=0"
        
        
  5. Since we're going to run a linux container, another important component is the emulators/linux_base-rl9 package. Do not forget to enable linux service via:
    
    service linux enable
    service linux start
    
    
  6. Next step is pull down ubuntu:24.04 image from the Docker registry:
    
    % doas podman pull --os=linux --arch=amd64 docker.io/library/ubuntu:24.04
    Trying to pull docker.io/library/ubuntu:24.04...
    Getting image source signatures
    Copying blob 4b3ffd8ccb52 skipped: already exists  
    Copying config 97bed23a34 done   | 
    Writing manifest to image destination
    97bed23a34971024aa8d254abbe67b7168772340d1f494034773bc464e8dd5b6
    
    
  7. It's time to run the ubuntu:24.04 container:
    
    % doas podman run --rm -it --os=linux --arch=amd64 docker.io/library/ubuntu:24.04
    root@8313a0296da2:/
    
    
    So, the container is running interactively now, we can move forward to the next phase.

Phase 2: The Core Build Process

It's necessary to install build tools, such as gcc compiler and libraries in a running container:


root@8313a0296da2:/ echo 'APT::Cache-Limit "1000000000";' > /etc/apt/apt.conf.d/70debconf
root@8313a0296da2:/ echo 'APT::Cache-Start "500000000";' >> /etc/apt/apt.conf.d/70debconf
root@8313a0296da2:/ apt update
root@8313a0296da2:/ apt install build-essential file libpcre2-dev libssl-dev wget zlib1g-dev

The Coverity Scan distibution was copied to the running container and then it's been unpacked.

Phase 3: A Reproducible Build Creation

Now we know all Coverity Scan build parts, and it's time to combain all our knowledge in a Containerfile, a configuration file that automates the steps of creating a container image. The Containerfile(5) says that the file is similar to a Makefile, but from my perspective, that file is similar to a Dockerfile.


FROM ubuntu:24.04
MAINTAINER info@tipi.work
USER root

ARG PROJECT
ARG VERSION
ARG TOKEN

ENV PATH="/home/ubuntu/cov/bin:$PATH"

RUN echo 'APT::Cache-Limit "1000000000";\nAPT::Cache-Start "500000000";' \
         >/etc/apt/apt.conf.d/70debconf \
    && echo 'APT::Install-Recommends "false";\nAPT::Install-Suggests "false";' \
         >>/etc/apt/apt.conf.d/70debconf \
    && apt-get -y update \
    && apt-get -y install build-essential ca-certificates \
         file libpcre2-dev libssl-dev wget zlib1g-dev \
    && cd /home/ubuntu \
    && wget -qO- https://${PROJECT}.org/download/${PROJECT}-${VERSION}.tar.gz \
     | tar xvzf - -C . \
    && mkdir cov \
    && wget https://scan.coverity.com/download/linux64 \
         --post-data "token=${TOKEN}&project=${PROJECT}" -qO- \
     | tar xfvz - -C cov --strip-components=1 \
         --exclude=bazel --exclude=bin/nupkgs --exclude=closure-compiler \
         --exclude=dotnet --exclude=go --exclude=jars --exclude=jdk21 \
         --exclude=jdk23 --exclude=jre --exclude=node --exclude=template-da \
         --exclude=support-angularjs \
    && cd ${PROJECT}-${VERSION} \
    && ./configure \
    && cov-build --dir cov-int make -j 4 \
    && tar cfvz ${PROJECT}.tgz cov-int

Important note: in case you're using ipfw(8) on your FreeBSD system, you may need to update the Containerfile with the following patch:


--- Containerfile.orig	2025-11-13 22:29:43.213699000 -0500
+++ Containerfile	2025-11-13 22:45:31.218896000 -0500
@@ -8,7 +8,10 @@
 
 ENV PATH="/home/ubuntu/cov/bin:$PATH"
 
-RUN echo 'APT::Cache-Limit "1000000000";\nAPT::Cache-Start "500000000";' \
+COPY sysctl /tmp
+
+RUN /tmp/sysctl net.inet.ip.fw.enable=0 \
+    && echo 'APT::Cache-Limit "1000000000";\nAPT::Cache-Start "500000000";' \
          >/etc/apt/apt.conf.d/70debconf \
     && echo 'APT::Install-Recommends "false";\nAPT::Install-Suggests "false";' \
          >>/etc/apt/apt.conf.d/70debconf \

It's a small trick, but it's necessary because a jail inherits net.inet.ip.fw.enable state from a host system, please visit ipfw(8) manual page to get details.

It's possible to avoid such behaviour by switching from the ipfw(8) to the pf(4).

Conclusion

We successfully ran a linux version of the Coverity Scan build software in an Ubuntu container under FreeBSD.