From 5803dfda886a39f3ca8f81a84d64a84c513a3f13 Mon Sep 17 00:00:00 2001 From: Jahnvi Thakkar Date: Mon, 15 Jun 2026 10:53:03 +0530 Subject: [PATCH 1/2] Fix ASAN ODR violation when loading both extensions (GH#1632) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add -fvisibility=hidden to CXXFLAGS in both config.m4 files. This makes all symbols library-private by default, preventing ASAN from detecting duplicate global definitions when sqlsrv.so and pdo_sqlsrv.so are loaded together. The get_module() entry point remains exported via ZEND_DLEXPORT which explicitly sets visibility(default). This is not a regression — the ODR violation has existed since the first commit (v2.0, 2010) but was only detected now that ASAN-enabled PHP builds are being created for the setup-php GitHub Action. Fixes #1632 --- source/pdo_sqlsrv/config.m4 | 1 + source/sqlsrv/config.m4 | 1 + 2 files changed, 2 insertions(+) diff --git a/source/pdo_sqlsrv/config.m4 b/source/pdo_sqlsrv/config.m4 index 11bfcd01d..78b017cda 100644 --- a/source/pdo_sqlsrv/config.m4 +++ b/source/pdo_sqlsrv/config.m4 @@ -75,6 +75,7 @@ if test "$PHP_PDO_SQLSRV" != "no"; then CXXFLAGS="$CXXFLAGS -std=c++11" CXXFLAGS="$CXXFLAGS -D_FORTIFY_SOURCE=2 -O2" CXXFLAGS="$CXXFLAGS -fstack-protector" + CXXFLAGS="$CXXFLAGS -fvisibility=hidden" HOST_OS_ARCH=`uname` if test "${HOST_OS_ARCH}" = "Darwin"; then diff --git a/source/sqlsrv/config.m4 b/source/sqlsrv/config.m4 index b71afc355..4bd86d8ef 100644 --- a/source/sqlsrv/config.m4 +++ b/source/sqlsrv/config.m4 @@ -55,6 +55,7 @@ if test "$PHP_SQLSRV" != "no"; then CXXFLAGS="$CXXFLAGS -std=c++11" CXXFLAGS="$CXXFLAGS -D_FORTIFY_SOURCE=2 -O2" CXXFLAGS="$CXXFLAGS -fstack-protector" + CXXFLAGS="$CXXFLAGS -fvisibility=hidden" HOST_OS_ARCH=`uname` if test "${HOST_OS_ARCH}" = "Darwin"; then From f8e3d4476d4818f0cbbfdef2dc29f060072b5323 Mon Sep 17 00:00:00 2001 From: Jahnvi Thakkar Date: Mon, 15 Jun 2026 10:53:15 +0530 Subject: [PATCH 2/2] Add ASAN test Dockerfile for ODR violation testing Builds PHP from source with --enable-address-sanitizer, compiles both extensions with -fsanitize=address, and verifies they load together without ASAN errors. Useful for validating future changes against ASAN. --- Dockerfile-asan-test | 99 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 Dockerfile-asan-test diff --git a/Dockerfile-asan-test b/Dockerfile-asan-test new file mode 100644 index 000000000..48a1dafc6 --- /dev/null +++ b/Dockerfile-asan-test @@ -0,0 +1,99 @@ +# Dockerfile for reproducing the ASAN ODR violation (GitHub Issue #1632) +# +# This builds PHP from source with AddressSanitizer enabled, then builds +# both sqlsrv and pdo_sqlsrv extensions and attempts to load them together. +# +# Usage: +# docker build -f Dockerfile-asan-test -t msphpsql-asan-test . +# docker run --rm msphpsql-asan-test +# +# Expected results: +# BEFORE fix: ASAN aborts with "odr-violation" on g_sqlsrv_stream_wrapper / isVistaOrGreater +# AFTER fix: php -v succeeds, both extensions load cleanly + +FROM ubuntu:24.04 + +ENV DEBIAN_FRONTEND=noninteractive +ENV PHP_VERSION=8.4.8 + +# Install build dependencies +RUN apt-get update && apt-get install -y \ + autoconf \ + bison \ + build-essential \ + curl \ + libcurl4-openssl-dev \ + libonig-dev \ + libreadline-dev \ + libsqlite3-dev \ + libssl-dev \ + libxml2-dev \ + pkg-config \ + re2c \ + unixodbc-dev \ + zlib1g-dev \ + && apt-get clean + +# Download and build PHP from source with ASAN +WORKDIR /tmp +RUN curl -fSL https://www.php.net/distributions/php-${PHP_VERSION}.tar.gz -o php.tar.gz \ + && tar xzf php.tar.gz \ + && cd php-${PHP_VERSION} \ + && ./configure \ + --prefix=/usr/local \ + --enable-debug \ + --enable-address-sanitizer \ + --enable-pdo \ + --with-pdo-odbc=unixODBC,/usr \ + --without-pear \ + --disable-cgi \ + --disable-phpdbg \ + && make -j$(nproc) \ + && make install \ + && cd / && rm -rf /tmp/php* + +# Verify PHP was built with ASAN +RUN php -v 2>&1 | head -5 || true + +# Copy extension source and clean any build artifacts +WORKDIR /build +COPY source/ /build/source/ +RUN cd /build/source/sqlsrv && rm -rf .libs build modules *.lo *.la *.dep Makefile* config.h config.log config.nice config.status configure configure~ libtool run-tests.php acinclude.m4 aclocal.m4 autom4te.cache 2>/dev/null; true +RUN cd /build/source/pdo_sqlsrv && rm -rf .libs build modules *.lo *.la *.dep Makefile* config.h config.log config.nice config.status configure configure~ libtool run-tests.php acinclude.m4 aclocal.m4 autom4te.cache 2>/dev/null; true + +# Run packagize to copy shared/ into each extension dir +WORKDIR /build/source +RUN sed -i 's/\r$//' packagize.sh && chmod +x packagize.sh && bash packagize.sh + +# Build sqlsrv extension (with ASAN flags to match PHP) +WORKDIR /build/source/sqlsrv +RUN phpize \ + && ./configure CXXFLAGS="-fsanitize=address -fno-omit-frame-pointer -g" LDFLAGS="-fsanitize=address" \ + && make -j$(nproc) \ + && make install + +# Build pdo_sqlsrv extension (with ASAN flags to match PHP) +WORKDIR /build/source/pdo_sqlsrv +RUN phpize \ + && ./configure CXXFLAGS="-fsanitize=address -fno-omit-frame-pointer -g" LDFLAGS="-fsanitize=address" \ + && make -j$(nproc) \ + && make install + +# Test: try to load both extensions together +# This is the step that triggers the ODR violation under ASAN +WORKDIR / +RUN echo "=== Testing: load both extensions ===" \ + && echo "If ASAN is working, this will either succeed (fix works) or abort (ODR violation):" \ + && ASAN_OPTIONS=detect_odr_violation=2 php -d extension=sqlsrv.so -d extension=pdo_sqlsrv.so -v 2>&1; \ + echo "Exit code: $?" + +# Also test each extension individually (should always work) +RUN echo "=== Testing: sqlsrv only ===" \ + && php -d extension=sqlsrv.so -r "echo 'sqlsrv loaded OK' . PHP_EOL;" 2>&1; \ + echo "Exit code: $?" + +RUN echo "=== Testing: pdo_sqlsrv only ===" \ + && php -d extension=pdo_sqlsrv.so -r "echo 'pdo_sqlsrv loaded OK' . PHP_EOL;" 2>&1; \ + echo "Exit code: $?" + +CMD ["bash", "-c", "echo 'ASAN ODR test complete. Check build output above.'"]