• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Debugging Segment Violations: Profiling Custom PHP Extensions with GDB, Valgrind, and AddressSanitizer

Debugging Segment Violations: Profiling Custom PHP Extensions with GDB, Valgrind, and AddressSanitizer

Compiling PHP with Debug Symbols

Before we can effectively debug a custom PHP extension causing segmentation faults, we need to ensure PHP itself is compiled with debugging symbols. This allows tools like GDB and Valgrind to map memory addresses back to source code lines. If you’re using a pre-compiled PHP binary from a package manager, it’s highly unlikely to have these symbols enabled. The most reliable approach is to compile PHP from source.

The key flags for enabling debugging are --enable-debug and --with-debug=yes. Additionally, for Valgrind and AddressSanitizer, it’s beneficial to compile with optimization level 0 (-O0) and the -g flag for maximum debug information. While --enable-debug is often sufficient, explicitly setting optimization and debug flags provides the most granular control.

Here’s a typical configuration and compilation sequence:

1. Download PHP Source

Obtain the source code for the specific PHP version you are targeting. It’s crucial to match the version of your running PHP interpreter.

wget https://www.php.net/distributions/php-8.2.10.tar.gz
tar -xzf php-8.2.10.tar.gz
cd php-8.2.10

2. Configure PHP

The ./configure script prepares the build environment. We’ll include the necessary flags for debugging. If your extension has specific dependencies (e.g., OpenSSL, libxml2), ensure you include their respective --with- flags.

./configure \
    --prefix=/usr/local/php-debug \
    --enable-debug \
    --with-debug=yes \
    --disable-all \
    --enable-cli \
    --enable-fpm \
    --with-openssl \
    --with-zlib \
    --enable-mbstring \
    --enable-sockets \
    --enable-pcntl \
    --enable-sysvmsg \
    --enable-sysvsem \
    --enable-sysvshm \
    --enable-tokenizer \
    --enable-xml \
    --enable-json \
    --enable-phar \
    --enable-session \
    --enable-opcache \
    CFLAGS="-g -O0" \
    CXXFLAGS="-g -O0" \
    LDFLAGS="-g -O0"

Note: --prefix specifies the installation directory. -O0 disables optimizations, and -g includes debugging information. --disable-all --enable-cli --enable-fpm is a common pattern to build only the essential components.

3. Compile and Install

Use make to compile and make install to deploy the binaries and libraries to the specified prefix.

make -j$(nproc)
sudo make install

After installation, ensure your PATH environment variable includes /usr/local/php-debug/bin or adjust your system’s configuration to use this debug build of PHP.

Profiling with GDB: Step-by-Step Segmentation Fault Analysis

GDB (GNU Debugger) is an indispensable tool for analyzing crashes. When your PHP script segfaults, GDB can attach to the process, capture the state at the moment of the crash, and provide a backtrace to pinpoint the offending code.

1. Reproducing the Crash under GDB

First, identify the PHP script that triggers the segmentation fault. Then, launch GDB and tell it to execute your PHP interpreter with the script as an argument. If the crash is intermittent, you might need to run the script multiple times within the GDB session.

gdb /usr/local/php-debug/bin/php
(gdb) run your_script.php

When the segmentation fault occurs, GDB will stop execution and present a prompt. If the crash doesn’t happen immediately, you might need to run the script repeatedly:

(gdb) run your_script.php
... (script runs) ...
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7b2a123 in some_function_in_your_extension (arg1=0x0, arg2=...) at /path/to/your/extension/source.c:123

2. Obtaining a Backtrace

The most critical command at this point is bt (backtrace). This will show the call stack leading up to the crash, including function names, arguments, and line numbers if debug symbols are present.

(gdb) bt
#0  0x00007ffff7b2a123 in some_function_in_your_extension (arg1=0x0, arg2=...) at /path/to/your/extension/source.c:123
#1  0x00007ffff7b2a456 in another_function_in_your_extension (arg1=0x12345678, arg2=...) at /path/to/your/extension/source.c:234
#2  0x0000555555678901 in execute_scripts (argc=2, argv=0x7fffffffdc78) at /path/to/php/source/zend/zend_execute.c:2500
#3  0x0000555555678901 in zend_do_fcall_code (execute_data=0x7ffff7f01000) at /path/to/php/source/zend/zend_vm_execute.c:1234
#4  ... (more frames) ...

Examine the output carefully. Frames originating from your custom extension (e.g., some_function_in_your_extension) are your primary focus. The line number (e.g., source.c:123) will point you directly to the problematic code. If you see ??:0 or ???, it indicates missing debug symbols for that part of the code.

3. Inspecting Variables and Memory

Once you’ve identified the crashing function, you can inspect the state of variables and memory. Use the frame command to switch to a specific stack frame, and then print (or p) to view variable values.

(gdb) frame #0
#0  0x00007ffff7b2a123 in some_function_in_your_extension (arg1=0x0, arg2=...) at /path/to/your/extension/source.c:123
(gdb) p arg1
$1 = (void *) 0x0
(gdb) p arg2
$2 = { member1 = 10, member2 = "some_string" }
(gdb) x/10xg arg2  // Examine memory at address of arg2 in hex
0x7fffffffdc80: 0x000000000000000a 0x0000000000000000

A common cause of segfaults is dereferencing a null pointer (as seen with arg1 = 0x0 above). Inspecting the values of pointers and structures passed into your functions is crucial for understanding why an invalid memory access occurred.

Memory Error Detection with Valgrind

Valgrind is a powerful instrumentation framework that can detect memory management errors such as use of uninitialized memory, reading/writing memory after it has been freed, and memory leaks. It runs your program in a simulated environment, adding overhead but providing invaluable insights.

1. Running PHP with Valgrind

Execute your PHP script using the valgrind command. Ensure you are using the debug-compiled PHP binary.

valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --track-origins=yes /usr/local/php-debug/bin/php your_script.php

Key Valgrind options:

  • --tool=memcheck: Specifies the Valgrind tool to use (memory checking).
  • --leak-check=full: Performs a more thorough leak check.
  • --show-leak-kinds=all: Reports all types of leaks (definite, indirect, possible, reachable).
  • --track-origins=yes: Attempts to track the origin of uninitialized values, which is extremely helpful for debugging.

2. Interpreting Valgrind Output

Valgrind will report any memory errors it detects. The output can be verbose, but it typically includes:

  • The type of error (e.g., Invalid read of size 8, Use of uninitialised value).
  • The memory address involved.
  • A stack trace pointing to the source code location where the error occurred.

Example of a Valgrind report:

==12345== Memcheck, a memory error detector
==12345== Copyright (C) 2000-2017, and GNU contributors
==12345== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==12345== Command: /usr/local/php-debug/bin/php your_script.php
==12345==
==12345== Invalid read of size 8
==12345==    at 0x400A8A: process_data (my_extension.c:45)
==12345==    by 0x555555678901: execute_scripts (zend/zend_execute.c:2500)
==12345==    by 0x555555678901: zend_do_fcall_code (zend/zend_vm_execute.c:1234)
==12345==  Address 0x0 is not stack'd, malloc'd or a block of zero bytes
==12345==
==12345== Use of uninitialised value as pointer in sync with 0x400A8A
==12345==    at 0x400A8A: process_data (my_extension.c:45)
==12345==    by 0x555555678901: execute_scripts (zend/zend_execute.c:2500)
==12345==  Uninitialised value was created by a heap allocation
==12345==    at 0x4C31BCF: malloc (vg_replace_malloc.c:309)
==12345==    by 0x400800: initialize_struct (my_extension.c:20)
==12345==
==12345== Process leaks: 0 bytes in 0 blocks
==12345== No leaks are possible.
==12345==
==12345== For counts of detected errors, rerun with: -v
==12345== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

In this example, process_data in my_extension.c at line 45 is attempting to read from a null pointer (Address 0x0) and also using an uninitialized value as a pointer. The trace shows how this uninitialized value was created during a malloc call within initialize_struct.

Detecting Memory Errors with AddressSanitizer (ASan)

AddressSanitizer (ASan) is a compiler instrumentation tool that detects memory errors at runtime. It’s generally faster than Valgrind and can catch a wider range of errors, including buffer overflows, use-after-free, and use-after-return. ASan requires compiler support (GCC or Clang) and is enabled during the compilation of PHP and your extension.

1. Compiling PHP with ASan Support

To enable ASan, you need to pass specific flags to the compiler during the PHP build process. These flags instruct the compiler to insert ASan’s instrumentation code.

# Ensure you have the ASan runtime library installed (e.g., libasan5 on Debian/Ubuntu)
# apt-get install libasan5

./configure \
    --prefix=/usr/local/php-asan \
    --enable-debug \
    --with-debug=yes \
    --disable-all \
    --enable-cli \
    --enable-fpm \
    --with-openssl \
    --with-zlib \
    --enable-mbstring \
    --enable-sockets \
    --enable-pcntl \
    --enable-sysvmsg \
    --enable-sysvsem \
    --enable-sysvshm \
    --enable-tokenizer \
    --enable-xml \
    --enable-json \
    --enable-phar \
    --enable-session \
    --enable-opcache \
    CFLAGS="-g -O0 -fsanitize=address" \
    CXXFLAGS="-g -O0 -fsanitize=address" \
    LDFLAGS="-g -O0 -fsanitize=address" \
    LIBS="-lasan"

make -j$(nproc)
sudo make install

The critical flags here are -fsanitize=address for CFLAGS/CXXFLAGS and linking with -lasan. This recompiles PHP with ASan instrumentation. If your extension is written in C/C++, you’ll need to recompile it with similar flags.

2. Compiling Your Custom Extension with ASan

If your extension is written in C/C++, you must compile it with ASan enabled. This typically involves setting the appropriate compiler and linker flags when building your extension. If you’re using PHP’s extension build system (phpize), you’ll need to ensure these flags are passed.

# Assuming you are in your extension's directory
export CFLAGS="-g -O0 -fsanitize=address"
export CXXFLAGS="-g -O0 -fsanitize=address"
export LDFLAGS="-g -O0 -fsanitize=address"
export LIBS="-lasan"

phpize
./configure
make -j$(nproc)
sudo make install

After recompiling both PHP and your extension with ASan, you can run your script as usual. ASan will automatically detect memory errors and report them.

3. Interpreting ASan Output

When ASan detects an error, it will print a detailed report to stderr. This report includes:

  • The type of memory error (e.g., heap-buffer-overflow, stack-buffer-overflow, use-after-free).
  • The exact memory address and size of the access.
  • A stack trace of the current execution path.
  • A stack trace of where the problematic memory was allocated or freed.

Example ASan report:

==12345==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000010 at pc 0x7f8c12345678 bp 0x7ffc12345678 sp 0x7ffc12345670
WRITE of size 8 at 0x602000000010 thread T0
    #0 0x7f8c12345678 in my_extension_function (/path/to/your/extension.so+0x1234)
    #1 0x555555678901 in execute_scripts (/usr/local/php-asan/bin/php+0x123456)
    #2 0x555555678901 in zend_do_fcall_code (/usr/local/php-asan/bin/php+0x789012)
    ...

    0x602000000010 is located 0 bytes to the right of 16-byte region [0x602000000000, 0x602000000010)
    allocated by thread T0.
    #0 0x7f8c12345678 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x123456)
    #1 0x7f8c12345678 in my_extension_init (/path/to/your/extension.so+0x5678)
    #2 0x555555678901 in zend_startup_module (/usr/local/php-asan/bin/php+0xabcdef)
    ...

Shadow bytes around the buggy address:
0x0804080408040804: 00 00 00 00 00 00 00 00
0x0804080408040810: 00 00 00 00 00 00 00 00
0x0804080408040820: 00 00 00 00 00 00 00 00
0x0804080408040830: 00 00 00 00 00 00 00 00
0x0804080408040840: 00 00 00 00 00 00 00 00
0x0804080408040850: 00 00 00 00 00 00 00 00
0x0804080408040860: 00 00 00 00 00 00 00 00
0x0804080408040870: 00 00 00 00 00 00 00 00
==12345== ABORTING

The report clearly indicates a heap-buffer-overflow in my_extension_function. It shows the address being written to (0x602000000010) and that this address is just beyond the allocated region. The second stack trace reveals that the memory was allocated by my_extension_init via malloc. This information is invaluable for identifying the exact line of code causing the overflow.

Choosing the Right Tool

Each tool has its strengths:

  • GDB: Essential for analyzing crashes (segmentation faults). It provides immediate insight into the program state at the point of failure and is crucial for understanding the call stack.
  • Valgrind: Excellent for detecting a broad range of memory errors, including leaks and uninitialized memory usage. It has higher overhead but is very thorough.
  • AddressSanitizer (ASan): Offers a good balance of speed and error detection capabilities. It’s particularly effective for buffer overflows and use-after-free bugs. Requires recompilation of PHP and your extension.

For segmentation faults, start with GDB. For general memory corruption issues, Valgrind or ASan are your go-to tools. Often, a combination of these tools will be necessary to fully diagnose and resolve complex memory-related bugs in custom PHP extensions.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Go Goroutines vs. Node.js Event Loop: Scaling I/O-Bound Microservices Under High Load
  • Elixir Phoenix vs. Go Gin: Concurrency Models and Fault Tolerance Under Peak Request Volume
  • Python Celery vs. Go Channels: Distributed Task Queue Overhead and Memory Reliability
  • Scala Pekko vs. Go Goroutines: Actor Model vs. CSP for Event-Driven Reactive Systems
  • Java Loom Virtual Threads vs. Go Goroutines: Under-the-Hood Scheduler and Thread Overhead Comparison

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (584)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (806)
  • PHP (5)
  • PHP Development (21)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (19)
  • Ruby on Rails (1)
  • Security & Compliance (543)
  • SEO & Growth (491)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (7)
  • WordPress Theme Development (357)

Recent Posts

  • Go Goroutines vs. Node.js Event Loop: Scaling I/O-Bound Microservices Under High Load
  • Elixir Phoenix vs. Go Gin: Concurrency Models and Fault Tolerance Under Peak Request Volume
  • Python Celery vs. Go Channels: Distributed Task Queue Overhead and Memory Reliability
  • Scala Pekko vs. Go Goroutines: Actor Model vs. CSP for Event-Driven Reactive Systems
  • Java Loom Virtual Threads vs. Go Goroutines: Under-the-Hood Scheduler and Thread Overhead Comparison
  • Rust Tokio async/await vs. Node.js Event Loop: Event-Driven Concurrency and CPU Yielding Models

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (806)
  • Debugging & Troubleshooting (584)
  • Security & Compliance (543)
  • SEO & Growth (491)
  • Business & Monetization (390)

Our Products

  • School Management & Student Administration System
  • Integrated Hospital & Clinic Management System
  • Real Estate Directory & Agent Portal
  • Restaurant POS & Table Booking System
  • Retail Inventory POS & Billing System
  • Pharmacy Inventory & Clinic Billing System

Our Services

  • Vibe Engineering & AI Code Auditing Services
  • Prompt Engineering & "Vibe Coding" Workflow Consulting
  • AI-Augmented "Vibe Coding" & Rapid MVP Development
  • Figma to Shopify Liquid Theme Customization
  • Figma to WooCommerce Frontend Development
  • Figma to Magento 2 Theme Development

Copyright © 2026 · Vinay Vengala