Compare commits

..
2 Commits
Author SHA1 Message Date
weaselbot 2431f7db8a Fix self-move-assignment and leak in ConflictSet move-assignment
The user-declared move-assignment operator overwrote `impl` without first
destroying the existing implementation object, leaking all memory and resources
owned by the left-hand side. Self-move-assignment also set `impl` to nullptr,
leaving the object invalid and leaking the old state.

Fix all three implementations (ConflictSet.cpp, SkipList.cpp, HashTable.cpp)
to guard against self-assignment and to destroy/free the old `impl` before
taking ownership of `other.impl`.
2026-06-21 20:43:44 -04:00
andrew 6d8b939a81 Replace CI Docker image with inline apt installs
Drops the build-image job and the private registry dependency entirely.
Each job now installs only the packages it needs, caching /var/cache/apt/archives
keyed on the workflow file and ~/.cache/pre-commit keyed on .pre-commit-config.yaml.
2026-06-21 19:21:04 -04:00
4 changed files with 83 additions and 67 deletions
+64 -64
View File
@@ -2,62 +2,38 @@ name: CI
on: [push, pull_request] on: [push, pull_request]
env:
CC: clang
CXX: clang++
jobs: jobs:
build-image:
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-latest-amd64
arch: amd64
- runner: ubuntu-latest-arm64
arch: arm64
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- name: Log in to registry
env:
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: |
echo "$REGISTRY_TOKEN" \
| docker login -u "$REGISTRY_USER" --password-stdin git.weaselab.dev
- name: Build and push image if changed
run: |
image=git.weaselab.dev/weaselab/conflict-set-ci
hash="$(sha256sum Dockerfile .pre-commit-config.yaml | sha256sum | cut -c 1-16)"
latest="$image:latest-${{ matrix.arch }}"
current="$(docker buildx imagetools inspect "$latest" \
--format '{{index .Image.Config.Labels "dev.weaselab.ci-hash"}}' 2> /dev/null || true)"
if [ "$current" = "$hash" ]; then
echo "$latest is up to date"
else
docker build --push --label "dev.weaselab.ci-hash=$hash" -t "$latest" .
fi
pre-commit: pre-commit:
needs: build-image
runs-on: ubuntu-latest-amd64 runs-on: ubuntu-latest-amd64
container:
image: git.weaselab.dev/weaselab/conflict-set-ci:latest-amd64
credentials:
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_TOKEN }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: /var/cache/apt/archives
key: apt-amd64-${{ hashFiles('.gitea/workflows/ci.yml') }}
- name: Install dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y \
clang git nodejs pre-commit
- uses: actions/cache@v4
with:
path: ~/.cache/pre-commit
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
- name: Run pre-commit - name: Run pre-commit
env:
# use the hooks pre-installed in the image
HOME: /tmp
run: | run: |
git config --global --add safe.directory "$PWD" git config --global --add safe.directory "$PWD"
pre-commit run --all-files --show-diff-on-failure pre-commit run --all-files --show-diff-on-failure
test: test:
needs: build-image
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -71,14 +47,23 @@ jobs:
- name: gcc - name: gcc
cmake_args: -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ cmake_args: -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++
runs-on: ubuntu-latest-amd64 runs-on: ubuntu-latest-amd64
container:
image: git.weaselab.dev/weaselab/conflict-set-ci:latest-amd64
credentials:
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_TOKEN }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: /var/cache/apt/archives
key: apt-amd64-${{ hashFiles('.gitea/workflows/ci.yml') }}
- name: Install dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y \
build-essential ccache clang cmake gcc g++ \
libc6-dbg llvm mold ninja-build python3 valgrind zstd
sudo curl -Ls "https://dl.min.io/client/mc/release/linux-amd64/mc" \
-o /usr/local/bin/mc && sudo chmod +x /usr/local/bin/mc
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
path: .ccache path: .ccache
@@ -120,7 +105,6 @@ jobs:
| tee -a "$GITHUB_STEP_SUMMARY" | tee -a "$GITHUB_STEP_SUMMARY"
release: release:
needs: build-image
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -130,14 +114,25 @@ jobs:
- runner: ubuntu-latest-arm64 - runner: ubuntu-latest-arm64
arch: arm64 arch: arm64
runs-on: ${{ matrix.runner }} runs-on: ${{ matrix.runner }}
container:
image: git.weaselab.dev/weaselab/conflict-set-ci:latest-${{ matrix.arch }}
credentials:
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_TOKEN }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: /var/cache/apt/archives
key: apt-${{ matrix.arch }}-${{ hashFiles('.gitea/workflows/ci.yml') }}
- name: Install dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y \
biber build-essential ccache clang cmake devscripts \
latexmk libc6-dbg llvm mold ninja-build rpm \
texlive-bibtex-extra texlive-fonts-recommended \
texlive-latex-extra texlive-pictures valgrind zstd
sudo curl -Ls "https://dl.min.io/client/mc/release/linux-$(dpkg --print-architecture)/mc" \
-o /usr/local/bin/mc && sudo chmod +x /usr/local/bin/mc
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
path: .ccache path: .ccache
@@ -188,9 +183,6 @@ jobs:
dest="minio/jenkins/conflict-set/${{ gitea.run_number }}/release-${{ matrix.arch }}/" dest="minio/jenkins/conflict-set/${{ gitea.run_number }}/release-${{ matrix.arch }}/"
zstd build/Testing/*/Test.xml zstd build/Testing/*/Test.xml
mc cp build/Testing/*/Test.xml.zst "$dest" mc cp build/Testing/*/Test.xml.zst "$dest"
# This step runs even when a previous step failed, to upload test
# results. The packages may never have been built though, so skip
# them if they're missing.
if compgen -G "build/*.deb" > /dev/null; then if compgen -G "build/*.deb" > /dev/null; then
mc cp build/*.deb "$dest" mc cp build/*.deb "$dest"
fi fi
@@ -209,16 +201,24 @@ jobs:
| tee -a "$GITHUB_STEP_SUMMARY" | tee -a "$GITHUB_STEP_SUMMARY"
coverage: coverage:
needs: build-image
runs-on: ubuntu-latest-amd64 runs-on: ubuntu-latest-amd64
container:
image: git.weaselab.dev/weaselab/conflict-set-ci:latest-amd64
credentials:
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_TOKEN }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: /var/cache/apt/archives
key: apt-amd64-${{ hashFiles('.gitea/workflows/ci.yml') }}
- name: Install dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y \
build-essential ccache clang cmake gcovr \
libc6-dbg llvm mold ninja-build python3 valgrind zstd
sudo curl -Ls "https://dl.min.io/client/mc/release/linux-amd64/mc" \
-o /usr/local/bin/mc && sudo chmod +x /usr/local/bin/mc
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
path: .ccache path: .ccache
+6 -1
View File
@@ -5589,7 +5589,12 @@ ConflictSet::ConflictSet(ConflictSet &&other) noexcept
: impl(std::exchange(other.impl, nullptr)) {} : impl(std::exchange(other.impl, nullptr)) {}
ConflictSet &ConflictSet::operator=(ConflictSet &&other) noexcept { ConflictSet &ConflictSet::operator=(ConflictSet &&other) noexcept {
impl = std::exchange(other.impl, nullptr); if (this != &other) {
if (impl) {
internal_destroy(impl);
}
impl = std::exchange(other.impl, nullptr);
}
return *this; return *this;
} }
+7 -1
View File
@@ -119,7 +119,13 @@ ConflictSet::ConflictSet(ConflictSet &&other) noexcept
: impl(std::exchange(other.impl, nullptr)) {} : impl(std::exchange(other.impl, nullptr)) {}
ConflictSet &ConflictSet::operator=(ConflictSet &&other) noexcept { ConflictSet &ConflictSet::operator=(ConflictSet &&other) noexcept {
impl = std::exchange(other.impl, nullptr); if (this != &other) {
if (impl) {
impl->~Impl();
safe_free(impl, sizeof(Impl));
}
impl = std::exchange(other.impl, nullptr);
}
return *this; return *this;
} }
+6 -1
View File
@@ -981,7 +981,12 @@ ConflictSet::ConflictSet(ConflictSet &&other) noexcept
: impl(std::exchange(other.impl, nullptr)) {} : impl(std::exchange(other.impl, nullptr)) {}
ConflictSet &ConflictSet::operator=(ConflictSet &&other) noexcept { ConflictSet &ConflictSet::operator=(ConflictSet &&other) noexcept {
impl = std::exchange(other.impl, nullptr); if (this != &other) {
if (impl) {
internal_destroy(impl);
}
impl = std::exchange(other.impl, nullptr);
}
return *this; return *this;
} }