Skip to content

igc64.dll access violation (0xC0000005) compiling SPIR-V that loads a 2+-member struct through a UBO-held BDA pointer, at -O1+ #445

Description

@dozeri83

Compiling a valid SPIR-V compute shader crashes  igc64.dll  with access violation  0xC0000005  during  vkCreateComputePipelines , but only at optimization  -O1  and above ( -O0  works fine). The trigger: a struct containing a  PhysicalStorageBuffer  (buffer-device-address) pointer member is loaded from an array reached via another BDA pointer stored in a UBO, then the pointer member is dereferenced. Bisection shows the crash depends on the loaded struct having 2 or more members — a single-pointer-only struct survives; adding any second member (any type) reproduces the crash. Minimal Slang source, compiled  .spv  (crashing and surviving variants), and a self-contained Vulkan harness are attached. Verified 3/3 crashes at  -O3 , 0/3 at  -O0 .
Environment:
• GPU: Intel(R) Iris(R) Xe Graphics (Tiger Lake)
• Driver version: 32.0.101.7085
• Vulkan API version: 1.4.323
• OS: Windows
• Faulting module:  igc64.dll 
• Exception:  0xC0000005  (access violation), constant fault offset
• Trigger call:  vkCreateComputePipelines 

attached files:
repro_min.slang  — minimal shader source that reproduce the issue

// Variant 2: remove the index-table indirection entirely -- index the
// pointer array directly with globalSplatID.

struct SplatSetDesc
{
  float*   centersAddress;
  uint64_t colorsAddress;
};

struct SceneAssets
{
  SplatSetDesc* splatSetDescriptors;
};

[[vk::binding(0, 0)]] ConstantBuffer<SceneAssets> assets;
[[vk::binding(1, 0)]] RWStructuredBuffer<float4> outBuf;

[numthreads(64, 1, 1)]
[shader("compute")]
void main(uint3 dispatchThreadID : SV_DispatchThreadID)
{
  const uint globalSplatID = dispatchThreadID.x;

  SplatSetDesc desc = assets.splatSetDescriptors[globalSplatID];

  float* ptr = (float*)(uint64_t(desc.centersAddress) + uint64_t(globalSplatID) * 12ul);
  float3 center = float3(ptr[0], ptr[1], ptr[2]);

  outBuf[globalSplatID] = float4(center, float(desc.colorsAddress));
}

self contained harnses cpp

// repro_harness.cpp
//
// Self-contained, minimal Vulkan harness to reproduce an igc64.dll access
// violation (0xC0000005) when creating a compute pipeline from SPIR-V that
// loads a struct (containing a pointer/BDA member) through a buffer-device-
// address pointer that was itself read from a uniform buffer.
//
// Build (from a "x64 Native Tools Command Prompt for VS" or with vcvars64
// sourced):
//   cl /nologo /EHsc /std:c++17 repro_harness.cpp ^
//      /I "%VULKAN_SDK%\Include" ^
//      /link /LIBPATH:"%VULKAN_SDK%\Lib" vulkan-1.lib /out:repro_harness.exe
//
// Usage:
//   repro_harness.exe repro.spv
//
// The pipeline creation call is wrapped in Windows SEH (__try/__except) so
// the harness can catch the access violation and report CRASHED/SURVIVED
// instead of the whole process dying with no diagnostic output.

#define VK_USE_PLATFORM_WIN32_KHR
#include <vulkan/vulkan.h>

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <vector>
#include <windows.h>

static std::vector<uint32_t> readSpirv(const char* path) {
    std::ifstream f(path, std::ios::binary | std::ios::ate);
    if (!f) {
        fprintf(stderr, "Failed to open %s\n", path);
        exit(2);
    }
    size_t size = static_cast<size_t>(f.tellg());
    f.seekg(0);
    std::vector<uint32_t> code(size / sizeof(uint32_t));
    f.read(reinterpret_cast<char*>(code.data()), size);
    return code;
}

// Runs vkCreateComputePipelines guarded by SEH so a driver-side access
// violation (0xC0000005 in igc64.dll) is caught here instead of killing the
// process with no output.
static int createPipelineGuarded(VkDevice device, VkPipelineLayout layout,
                                  VkShaderModule module,
                                  VkPipeline* outPipeline) {
    VkPipelineShaderStageCreateInfo stage{};
    stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
    stage.module = module;
    stage.pName = "main";

    VkComputePipelineCreateInfo ci{};
    ci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
    ci.stage = stage;
    ci.layout = layout;

    __try {
        VkResult r = vkCreateComputePipelines(device, VK_NULL_HANDLE, 1, &ci,
                                               nullptr, outPipeline);
        if (r != VK_SUCCESS) {
            printf("vkCreateComputePipelines returned VkResult=%d\n", r);
            return 1;
        }
        return 0;
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        DWORD code = GetExceptionCode();
        printf("CRASHED: SEH exception 0x%08lX during vkCreateComputePipelines "
               "(this is the igc64.dll access violation)\n",
               code);
        return -1;
    }
}

int main(int argc, char** argv) {
    if (argc < 2) {
        fprintf(stderr, "usage: %s <shader.spv>\n", argv[0]);
        return 2;
    }

    std::vector<uint32_t> spirv = readSpirv(argv[1]);
    printf("Loaded %s (%zu bytes)\n", argv[1], spirv.size() * sizeof(uint32_t));

    // --- Instance ---
    VkApplicationInfo appInfo{};
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    appInfo.pApplicationName = "igc-repro";
    appInfo.apiVersion = VK_API_VERSION_1_2;

    VkInstanceCreateInfo instCi{};
    instCi.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    instCi.pApplicationInfo = &appInfo;

    VkInstance instance;
    if (vkCreateInstance(&instCi, nullptr, &instance) != VK_SUCCESS) {
        fprintf(stderr, "vkCreateInstance failed\n");
        return 2;
    }

    // --- Pick the first physical device (edit index below if you have
    //     multiple GPUs and want to target a specific one) ---
    uint32_t gpuCount = 0;
    vkEnumeratePhysicalDevices(instance, &gpuCount, nullptr);
    if (gpuCount == 0) {
        fprintf(stderr, "No Vulkan physical devices found\n");
        return 2;
    }
    std::vector<VkPhysicalDevice> gpus(gpuCount);
    vkEnumeratePhysicalDevices(instance, &gpuCount, gpus.data());

    VkPhysicalDevice phys = gpus[0];
    VkPhysicalDeviceProperties props{};
    vkGetPhysicalDeviceProperties(phys, &props);
    printf("Using GPU: %s (driverVersion=0x%08X, apiVersion=0x%08X)\n",
           props.deviceName, props.driverVersion, props.apiVersion);

    // --- Logical device with bufferDeviceAddress enabled ---
    float prio = 1.0f;
    VkDeviceQueueCreateInfo qci{};
    qci.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
    qci.queueFamilyIndex = 0;
    qci.queueCount = 1;
    qci.pQueuePriorities = &prio;

    VkPhysicalDeviceBufferDeviceAddressFeatures bdaFeatures{};
    bdaFeatures.sType =
        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES;
    bdaFeatures.bufferDeviceAddress = VK_TRUE;

    const char* deviceExts[] = {"VK_KHR_buffer_device_address"};

    VkDeviceCreateInfo devCi{};
    devCi.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
    devCi.pNext = &bdaFeatures;
    devCi.queueCreateInfoCount = 1;
    devCi.pQueueCreateInfos = &qci;
    devCi.enabledExtensionCount = 1;
    devCi.ppEnabledExtensionNames = deviceExts;

    VkDevice device;
    VkResult devResult = vkCreateDevice(phys, &devCi, nullptr, &device);
    if (devResult != VK_SUCCESS) {
        fprintf(stderr, "vkCreateDevice failed: %d\n", devResult);
        return 2;
    }

    // --- Shader module ---
    VkShaderModuleCreateInfo smCi{};
    smCi.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
    smCi.codeSize = spirv.size() * sizeof(uint32_t);
    smCi.pCode = spirv.data();

    VkShaderModule module;
    if (vkCreateShaderModule(device, &smCi, nullptr, &module) != VK_SUCCESS) {
        fprintf(stderr, "vkCreateShaderModule failed\n");
        return 2;
    }

    // --- Descriptor set layout matching the shader's bindings (UBO @0,
    //     storage buffer @1). No actual buffers need to be bound to reach
    //     the crash -- it happens purely during shader compilation. ---
    VkDescriptorSetLayoutBinding bindings[2]{};
    bindings[0].binding = 0;
    bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
    bindings[0].descriptorCount = 1;
    bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;

    bindings[1].binding = 1;
    bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
    bindings[1].descriptorCount = 1;
    bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;

    VkDescriptorSetLayoutCreateInfo dslCi{};
    dslCi.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
    dslCi.bindingCount = 2;
    dslCi.pBindings = bindings;

    VkDescriptorSetLayout dsl;
    vkCreateDescriptorSetLayout(device, &dslCi, nullptr, &dsl);

    VkPipelineLayoutCreateInfo plCi{};
    plCi.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
    plCi.setLayoutCount = 1;
    plCi.pSetLayouts = &dsl;

    VkPipelineLayout layout;
    vkCreatePipelineLayout(device, &plCi, nullptr, &layout);

    // --- The call under test ---
    VkPipeline pipeline = VK_NULL_HANDLE;
    int rc = createPipelineGuarded(device, layout, module, &pipeline);

    if (rc == 0) {
        printf("SURVIVED: pipeline created successfully\n");
    } else if (rc > 0) {
        printf("FAILED (clean VkResult error, not a crash)\n");
    }
    // rc < 0: CRASHED, already printed above.

    return rc < 0 ? 1 : 0;
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions