Merge branch 'ekilmer/run-asan-v2': Add ASAN/UBSAN and libc++ assertions CI

Add sanitizer and assertions CI jobs, fix UBSAN signed left shift
errors, and port the compareFinalOrder strict-weak ordering patch
to stable. Drop redundant Linux/macOS Debug builds in favor of
these more targeted checks.

* ekilmer/run-asan-v2:
  Add compareFinalOrder strict-weak ordering patch to stable
  Drop Debug builds on Linux and macOS in favor of sanitizer/assertions
  Install LLVM explicitly in sanitizer and assertions CI jobs
  Add libc++ assertions CI job for strict-weak ordering checks
  Add ASAN/UBSAN CI testing and fix signed left shift UB
This commit is contained in:
Eric Kilmer
2026-02-25 23:05:06 +00:00
6 changed files with 339 additions and 0 deletions
+182
View File
@@ -13,6 +13,9 @@ on:
branches:
- "*"
env:
LLVM_VERSION: 21
jobs:
build:
runs-on: ${{ matrix.os }}
@@ -23,6 +26,11 @@ jobs:
os: [ubuntu-latest, macos-latest, windows-latest]
build_type: [RelWithDebInfo, Debug]
release: [stable, HEAD]
exclude:
- os: ubuntu-latest
build_type: Debug
- os: macos-latest
build_type: Debug
steps:
- uses: actions/checkout@v6
@@ -254,3 +262,177 @@ jobs:
- name: ccache stats
run: ccache -s
sanitizer:
runs-on: ubuntu-latest
permissions:
contents: read
env:
ASAN_OPTIONS: strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:detect_leaks=1
UBSAN_OPTIONS: print_stacktrace=1
strategy:
fail-fast: false
matrix:
release: [stable, HEAD]
steps:
- uses: actions/checkout@v6
- name: Setup Git User for Applying Patches
run: |
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
- name: Install LLVM
run: |
wget -qO /tmp/llvm.sh https://apt.llvm.org/llvm.sh
chmod +x /tmp/llvm.sh
sudo /tmp/llvm.sh $LLVM_VERSION
echo "CXX=clang++-$LLVM_VERSION" >> "$GITHUB_ENV"
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y ccache
- name: Generate cache key
shell: cmake -P {0}
run: |
string(TIMESTAMP current_date "%Y-%m-%d-%H;%M;%S" UTC)
file(APPEND "$ENV{GITHUB_ENV}" "CACHE_KEY=sanitizer_${{ matrix.release }}\n")
file(APPEND "$ENV{GITHUB_ENV}" "CACHE_TIMESTAMP=${current_date}\n")
- name: Update the cache (ccache)
uses: actions/cache@v5
with:
path: "${{ github.workspace }}/ccache"
key: ${{ env.CACHE_KEY }}_ccache_${{ env.CACHE_TIMESTAMP }}
restore-keys: |
${{ env.CACHE_KEY }}_ccache_
- name: Setup ccache
working-directory: "${{ github.workspace }}"
shell: cmake -P {0}
run: |
file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/ccache")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_BASEDIR=${CMAKE_CURRENT_SOURCE_DIR}\n")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/ccache\n")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_COMPRESS=true\n")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_COMPRESSLEVEL=10\n")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_MAXSIZE=400M\n")
file(APPEND "$ENV{GITHUB_ENV}" "CMAKE_CXX_COMPILER_LAUNCHER=ccache\n")
file(APPEND "$ENV{GITHUB_ENV}" "CMAKE_C_COMPILER_LAUNCHER=ccache\n")
execute_process(COMMAND ccache -z)
- name: Configure the project
run: cmake --preset=ci-sanitize
-Dsleigh_RELEASE_TYPE=${{ matrix.release }}
-Dsleigh_BUILD_DOCUMENTATION=OFF
- name: Build the project
run: cmake
--build build/sanitize
-j 4
-v
- name: Test the project
working-directory: build/sanitize
run: ctest -VV
- name: Run the example
run: cmake
--build build/sanitize
-j 4
--target sleigh_example_runner
- name: Run the install target
run: cmake --install build/sanitize
--prefix install
- name: Smoketest sleigh lift
run: |
./install/bin/sleigh-lift --version
./install/bin/sleigh-lift disassemble x86-64.sla 4881ecc00f0000
./install/bin/sleigh-lift pcode x86-64.sla 4881ecc00f0000
- name: ccache stats
run: ccache -s
assertions:
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
fail-fast: false
matrix:
release: [stable, HEAD]
steps:
- uses: actions/checkout@v6
- name: Setup Git User for Applying Patches
run: |
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
- name: Install LLVM
run: |
wget -qO /tmp/llvm.sh https://apt.llvm.org/llvm.sh
chmod +x /tmp/llvm.sh
sudo /tmp/llvm.sh $LLVM_VERSION
echo "CXX=clang++-$LLVM_VERSION" >> "$GITHUB_ENV"
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y ccache libc++-$LLVM_VERSION-dev libc++abi-$LLVM_VERSION-dev
- name: Generate cache key
shell: cmake -P {0}
run: |
string(TIMESTAMP current_date "%Y-%m-%d-%H;%M;%S" UTC)
file(APPEND "$ENV{GITHUB_ENV}" "CACHE_KEY=assertions_${{ matrix.release }}\n")
file(APPEND "$ENV{GITHUB_ENV}" "CACHE_TIMESTAMP=${current_date}\n")
- name: Update the cache (ccache)
uses: actions/cache@v5
with:
path: "${{ github.workspace }}/ccache"
key: ${{ env.CACHE_KEY }}_ccache_${{ env.CACHE_TIMESTAMP }}
restore-keys: |
${{ env.CACHE_KEY }}_ccache_
- name: Setup ccache
working-directory: "${{ github.workspace }}"
shell: cmake -P {0}
run: |
file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/ccache")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_BASEDIR=${CMAKE_CURRENT_SOURCE_DIR}\n")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/ccache\n")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_COMPRESS=true\n")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_COMPRESSLEVEL=10\n")
file(APPEND "$ENV{GITHUB_ENV}" "CCACHE_MAXSIZE=400M\n")
file(APPEND "$ENV{GITHUB_ENV}" "CMAKE_CXX_COMPILER_LAUNCHER=ccache\n")
file(APPEND "$ENV{GITHUB_ENV}" "CMAKE_C_COMPILER_LAUNCHER=ccache\n")
execute_process(COMMAND ccache -z)
- name: Configure the project
run: cmake --preset=ci-assertions
-Dsleigh_RELEASE_TYPE=${{ matrix.release }}
-Dsleigh_BUILD_DOCUMENTATION=OFF
- name: Build the project
run: cmake
--build build/assertions
-j 4
-v
- name: Test the project
working-directory: build/assertions
run: ctest -VV
- name: ccache stats
run: ccache -s
+11
View File
@@ -114,6 +114,17 @@
"CMAKE_CXX_FLAGS_SANITIZE": "-O2 -g -fsanitize=address,undefined -fno-omit-frame-pointer -fno-common"
}
},
{
"name": "ci-assertions",
"binaryDir": "${sourceDir}/build/assertions",
"inherits": ["ci-unix", "dev-mode"],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Assertions",
"CMAKE_CXX_FLAGS_ASSERTIONS": "-O0 -g -stdlib=libc++ -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG -fstack-protector-strong",
"CMAKE_EXE_LINKER_FLAGS_ASSERTIONS": "-stdlib=libc++ -lc++abi",
"CMAKE_SHARED_LINKER_FLAGS_ASSERTIONS": "-stdlib=libc++ -lc++abi"
}
},
{
"name": "ci-build",
"binaryDir": "${sourceDir}/build",
@@ -0,0 +1,57 @@
From 8afa7d89a368b358a5493b87804985ed9ac70a2f Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 25 Feb 2026 20:41:10 +0000
Subject: [PATCH] Fix UBSAN signed left shift errors
Cast signed values to unsigned before left shifting to avoid undefined
behavior when the signed value is negative or when the result cannot be
represented in the signed type.
Fixes runtime errors detected by UndefinedBehaviorSanitizer:
- address.hh sign_extend(): left shift of negative value
- address.hh sign_extend(): left shift cannot be represented in type 'intb'
- slghpatexpress.cc LeftShiftExpression: left shift of negative value
---
Ghidra/Features/Decompiler/src/decompile/cpp/address.hh | 2 +-
.../Features/Decompiler/src/decompile/cpp/slghpatexpress.cc | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh
index 45144daf3..0e75c68b8 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh
@@ -544,7 +544,7 @@ inline intb sign_extend(intb val,int4 bit)
{
int4 sa = 8*sizeof(intb) - (bit+1);
- val = (val << sa) >> sa;
+ val = static_cast<intb>(static_cast<uintb>(val) << sa) >> sa;
return val;
}
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/slghpatexpress.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/slghpatexpress.cc
index 941097859..93cce378a 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/slghpatexpress.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/slghpatexpress.cc
@@ -969,7 +969,7 @@ intb LeftShiftExpression::getValue(ParserWalker &walker) const
{
intb leftval = getLeft()->getValue(walker);
intb rightval = getRight()->getValue(walker);
- return leftval << rightval;
+ return static_cast<intb>(static_cast<uintb>(leftval) << rightval);
}
intb LeftShiftExpression::getSubValue(const vector<intb> &replace,int4 &listpos) const
@@ -977,7 +977,7 @@ intb LeftShiftExpression::getSubValue(const vector<intb> &replace,int4 &listpos)
{
intb leftval = getLeft()->getSubValue(replace,listpos); // Must be left first
intb rightval = getRight()->getSubValue(replace,listpos);
- return leftval << rightval;
+ return static_cast<intb>(static_cast<uintb>(leftval) << rightval);
}
void LeftShiftExpression::encode(Encoder &encoder) const
--
2.51.1
@@ -0,0 +1,29 @@
From c435df4c0dbab80d8ff85e364cea9b59bf7010a1 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 25 Feb 2026 22:22:05 +0000
Subject: [PATCH] decompiler: Fix strict weak ordering compareFinalOrder
FlowBlock::compareFinalOrder returns true when both blocks have index 0,
violating strict weak ordering (irreflexivity). This is caught by libc++
debug mode hardening assertions during BlockGraph::orderBlocks sort.
---
Ghidra/Features/Decompiler/src/decompile/cpp/block.cc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc
index bf7103d91..2e495d9c3 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc
@@ -709,7 +709,7 @@ string FlowBlock::typeToName(FlowBlock::block_type bt)
bool FlowBlock::compareFinalOrder(const FlowBlock *bl1,const FlowBlock *bl2)
{
- if (bl1->getIndex() == 0) return true; // Make sure the entry point comes first
+ if (bl1->getIndex() == 0) return (bl2->getIndex() != 0); // Make sure the entry point comes first
if (bl2->getIndex() == 0) return false;
PcodeOp *op1 = bl1->lastOp();
PcodeOp *op2 = bl2->lastOp();
--
2.51.1
@@ -0,0 +1,57 @@
From 8afa7d89a368b358a5493b87804985ed9ac70a2f Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 25 Feb 2026 20:41:10 +0000
Subject: [PATCH] Fix UBSAN signed left shift errors
Cast signed values to unsigned before left shifting to avoid undefined
behavior when the signed value is negative or when the result cannot be
represented in the signed type.
Fixes runtime errors detected by UndefinedBehaviorSanitizer:
- address.hh sign_extend(): left shift of negative value
- address.hh sign_extend(): left shift cannot be represented in type 'intb'
- slghpatexpress.cc LeftShiftExpression: left shift of negative value
---
Ghidra/Features/Decompiler/src/decompile/cpp/address.hh | 2 +-
.../Features/Decompiler/src/decompile/cpp/slghpatexpress.cc | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh
index 45144daf3..0e75c68b8 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh
@@ -544,7 +544,7 @@ inline intb sign_extend(intb val,int4 bit)
{
int4 sa = 8*sizeof(intb) - (bit+1);
- val = (val << sa) >> sa;
+ val = static_cast<intb>(static_cast<uintb>(val) << sa) >> sa;
return val;
}
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/slghpatexpress.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/slghpatexpress.cc
index 941097859..93cce378a 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/slghpatexpress.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/slghpatexpress.cc
@@ -969,7 +969,7 @@ intb LeftShiftExpression::getValue(ParserWalker &walker) const
{
intb leftval = getLeft()->getValue(walker);
intb rightval = getRight()->getValue(walker);
- return leftval << rightval;
+ return static_cast<intb>(static_cast<uintb>(leftval) << rightval);
}
intb LeftShiftExpression::getSubValue(const vector<intb> &replace,int4 &listpos) const
@@ -977,7 +977,7 @@ intb LeftShiftExpression::getSubValue(const vector<intb> &replace,int4 &listpos)
{
intb leftval = getLeft()->getSubValue(replace,listpos); // Must be left first
intb rightval = getRight()->getSubValue(replace,listpos);
- return leftval << rightval;
+ return static_cast<intb>(static_cast<uintb>(leftval) << rightval);
}
void LeftShiftExpression::encode(Encoder &encoder) const
--
2.51.1
+3
View File
@@ -43,6 +43,8 @@ set(ghidra_patches
"${CMAKE_CURRENT_LIST_DIR}/patches/stable/0003-Ignore-floating-point-test-due-to-compilation-differ.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/stable/0004-Allow-positive-or-negative-NAN-in-decompiler-floatin.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/stable/0005-decompiler-Fix-strict-weak-ordering-TypePartialEnum.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/stable/0006-decompiler-Fix-strict-weak-ordering-compareFinalOrde.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/stable/0007-Fix-UBSAN-signed-left-shift-errors.patch"
)
# Ghidra pinned commits used for pinning last known working HEAD commit
@@ -65,6 +67,7 @@ if("${sleigh_RELEASE_TYPE}" STREQUAL "HEAD")
"${CMAKE_CURRENT_LIST_DIR}/patches/HEAD/0005-decompiler-Fix-strict-weak-ordering-TypePartialEnum.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/HEAD/0006-decompiler-Fix-strict-weak-ordering-PullRecord.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/HEAD/0007-decompiler-Fix-strict-weak-ordering-compareFinalOrde.patch"
"${CMAKE_CURRENT_LIST_DIR}/patches/HEAD/0008-Fix-UBSAN-signed-left-shift-errors.patch"
)
string(SUBSTRING "${ghidra_git_tag}" 0 7 ghidra_short_commit)
else()