Add ASAN/UBSAN CI testing and fix signed left shift UB

Add a sanitizer CI job that builds and tests with AddressSanitizer and
UndefinedBehaviorSanitizer using clang on Ubuntu for both stable and
HEAD release tracks.

Fix three UBSAN signed left shift errors found during sanitizer testing
by casting signed values to unsigned before left shifting:
- address.hh sign_extend(): left shift of negative value / overflow
- slghpatexpress.cc LeftShiftExpression: left shift of negative value

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Eric Kilmer
2026-02-25 20:55:01 +00:00
parent 4578ac2fba
commit b6adc0337a
4 changed files with 207 additions and 0 deletions
+91
View File
@@ -254,3 +254,94 @@ jobs:
- name: ccache stats
run: ccache -s
sanitizer:
runs-on: ubuntu-latest
permissions:
contents: read
env:
CXX: clang++
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 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
@@ -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,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
+2
View File
@@ -43,6 +43,7 @@ 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-Fix-UBSAN-signed-left-shift-errors.patch"
)
# Ghidra pinned commits used for pinning last known working HEAD commit
@@ -65,6 +66,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()