KhronosGroup / KhronosGroup/glslang
NULL Pointer Dereference in glslang::TIntermediate::isConversionAllowed
- Dominant language
- C++
- Stars
- 3.6k
- Forks
- 989
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 31
Description
Note: the [security advisory](https://github.com/KhronosGroup/glslang/security/advisories/new) will redirect to a 404 page.
So I report it in this.
## Affected Projects
glslang 15.1.0 ([https://github.com/KhronosGroup/glslang](https://github.com/KhronosGroup/glslang))
## Problem Type
CWE-476: NULL Pointer Dereference
## Description
### Summary
A null pointer read vulnerability was discovered in the `glslang::TIntermediate::isConversionAllowed` function within glslang. This issue occurs when processing certain files, leading to a potential application crash.
### Details
The vulnerability arises in the `glslang::TIntermediate::isConversionAllowed` function defined in `glslang/MachineIndependent/Intermediate.cpp` at line `541`.
The reason is that the function `H5O__cache_chk_serialize` did not check the validity of `node` before calling `node->getBasicType()`, resulting in a null pointer dereference read.
```c
bool TIntermediate::isConversionAllowed(TOperator op, TIntermTyped* node) const
{
//
// Does the base type even allow the operation?
//
switch (node->getBasicType()) { //null pointer read
case EbtVoid:
return false;
case EbtAtomicUint:
case EbtSampler:
case EbtAccStruct:
// opaque types can be passed to functions
if (op == EOpFunction)
break;
// HLSL can assign samplers directly (no constructor)
if (getSource() == EShSourceHlsl && node->getBasicType() == EbtSampler)
break;
// samplers can get assigned via a sampler constructor
// (well, not yet, but code in the rest of this function is ready for it)
if (node->getBasicType() == EbtSampler && op == EOpAssign &&
node->getAsOperator() != nullptr && node->getAsOperator()->getOp() == EOpConstructTextureSampler)
break;
// otherwise, opaque types can't even be operated on, let alone converted
return false;
default:
break;
}
return true;
}
```
### PoC
**Steps to reproduce:**
1. Clone the glslang repository and build it using the following commands :
```sh
export CC='clang'
export CXX='clang++'
export CFLAGS='-fsanitize=address -O0 -g'
export CXXFLAGS='-fsanitize=address -O0 -g'
mkdir -p build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_CTEST=ON -DCMAKE_VERBOSE_MAKEFILE=ON -DENABLE_OPT=0 ../
make V=1 -j4 install
```
2. Compile the fuzzer:
- harness compile_fuzzer.cc from oss-fuzz:
```c++
#include "glslang/Public/ResourceLimits.h"
#include "glslang/Public/ShaderLang.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
glslang::InitializeProcess();
EShMessages controls;
glslang::TShader shader(EShLangVertex);
const char *dataPtr = (const char*)data;
const int dataSize = (const int)size;
shader.setStringsWithLengths(&dataPtr, &dataSize, 1);
shader.setEntryPoint("ep");
// Parse the shader
shader.parse(GetDefaultResources(), 100, false, controls);
glslang::FinalizeProcess();
return 0;
}
```
```sh
export LIB_FUZZING_ENGINE='-fsanitize=fuzzer'
$CXX $CXXFLAGS $LIB_FUZZING_ENGINE $SRC/compile_fuzzer.cc \
-DENABLE_HLSL -DENABLE_OPT=0 -DGLSLANG_OSINCLUDE_UNIX \
-I$SRC/glslang/glslang/Include/ -I$SRC/glslang/glslang/.. \
-I$SRC/glslang/build/include -I$SRC/glslang/SPIRV/.. \
./glslang/libglslang.a ./SPIRV/libSPIRV.a \
./glslang/libglslang-default-resource-limits.a ./SPIRV/libSPVRemapper.a \
-lpthread ./glslang/libMachineIndependent.a \
./glslang/OSDependent/Unix/libOSDependent.a \
./glslang/libGenericCodeGen.a \
-o compile_fuzzer
```
3. Run the fuzzer to trigger the segmentation fault:
[isConversionAllowed-npd.zip](https://github.com/user-attachments/files/19587010/isConversionAllowed-npd.zip)
```
./compile_fuzzer ./isConversionAllowed-npd
```
This will cause AddressSanitizer to report a segmentation fault during the execution of the post-processing logic.
### ASAN Report
```sh
Running: ./isConversionAllowed-npd
AddressSanitizer:DEADLYSIGNAL
=================================================================
==5014==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5ca973cb1e33 bp 0x000000000000 sp 0x7ffca9dbe610 T0)
==5014==The signal is caused by a READ memory access.
==5014==Hint: address points to the zero page.
#0 0x5ca973cb1e33 in glslang::TIntermediate::isConversionAllowed(glslang::TOperator, glslang::TIntermTyped*) const /fuzz/project/glslang/glslang/MachineIndependent/Intermediate.cpp:541:19
#1 0x5ca973caac85 in glslang::TIntermediate::addPairConversion(glslang::TOperator, glslang::TIntermTyped*, glslang::TIntermTyped*) /fuzz/project/glslang/glslang/MachineIndependent/Intermediate.cpp:675:45
#2 0x5ca973cc3bbb in glslang::TIntermediate::addSelection(glslang::TIntermTyped*, glslang::TIntermTyped*, glslang::TIntermTyped*, glslang::TSourceLoc const&) /fuzz/project/glslang/glslang/MachineIndependent/Intermediate.cpp:2275:21
#3 0x5ca973b8bc3d in glslang::HlslGrammar::acceptConditionalExpression(glslang::TIntermTyped*&) /fuzz/project/glslang/glslang/HLSL/hlslGrammar.cpp:3236:25
#4 0x5ca973b87571 in glslang::HlslGrammar::acceptAssignmentExpression(glslang::TIntermTyped*&) /fuzz/project/glslang/glslang/HLSL/hlslGrammar.cpp:3158:11
#5 0x5ca973b885bd in glslang::HlslGrammar::acceptExpression(glslang::TIntermTyped*&) /fuzz/project/glslang/glslang/HLSL/hlslGrammar.cpp:3052:11
#6 0x5ca973b8bb07 in glslang::HlslGrammar::acceptConditionalExpression(glslang::TIntermTyped*&) /fuzz/project/glslang/glslang/HLSL/hlslGrammar.cpp:3217:11
#7 0x5ca973b87571 in glslang::HlslGrammar::acceptAssignmentExpression(glslang::TIntermTyped*&) /fuzz/project/glslang/glslang/HLSL/hlslGrammar.cpp:3158:11
#8 0x5ca973b8363d in glslang::HlslGrammar::acceptAttributes(glslang::TList&) /fuzz/project/glslang/glslang/HLSL/hlslGrammar.cpp:3883:20
#9 0x5ca973b7f0b1 in glslang::HlslGrammar::acceptDeclaration(TIntermNode*&) /fuzz/project/glslang/glslang/HLSL/hlslGrammar.cpp:362:5
#10 0x5ca973b7e402 in glslang::HlslGrammar::acceptDeclarationList(TIntermNode*&) /fuzz/project/glslang/glslang/HLSL/hlslGrammar.cpp:165:15
#11 0x5ca973b7e402 in glslang::HlslGrammar::acceptCompilationUnit() /fuzz/project/glslang/glslang/HLSL/hlslGrammar.cpp:130:11
#12 0x5ca973a722f0 in glslang::HlslParseContext::parseShaderStrings(glslang::TPpContext&, glslang::TInputScanner&, bool) /fuzz/project/glslang/glslang/HLSL/hlslParseHelper.cpp:132:18
#13 0x5ca97397685d in (anonymous namespace)::DoFullParse::operator()(glslang::TParseContextBase&, glslang::TPpContext&, glslang::TInputScanner&, bool, glslang::TSymbolTable&, glslang::TIntermediate&, EShOptimizationLevel, EShMessages) /fuzz/project/glslang/glslang/MachineIndependent/ShaderLang.cpp:1233:28
#14 0x5ca97397685d in bool (anonymous namespace)::ProcessDeferred<(anonymous namespace)::DoFullParse>(TCompiler*, char const* const*, int, int const*, char const* const*, char const*, EShOptimizationLevel, TBuiltInResource const*, int, EProfile, bool, int, bool, EShMessages, glslang::TIntermediate&, (anonymous namespace)::DoFullParse&, bool, glslang::TShader::Includer&, std::__cxx11::basic_string, std::allocator >, glslang::TEnvironment const*, bool) /fuzz/project/glslang/glslang/MachineIndependent/ShaderLang.cpp:1008:20
#15 0x5ca97397685d in (anonymous namespace)::CompileDeferred(TCompiler*, char const* const*, int, int const*, char const* const*, char const*, EShOptimizationLevel, TBuiltInResource const*, int, EProfile, bool, int, bool, EShMessages, glslang::TIntermediate&, glslang::TShader::Includer&, std::__cxx11::basic_string, std::allocator >, glslang::TEnvironment*, bool) /fuzz/project/glslang/glslang/MachineIndependent/ShaderLang.cpp:1321:12
#16 0x5ca97397b2ff in glslang::TShader::parse(TBuiltInResource const*, int, EProfile, bool, bool, EShMessages, glslang::TShader::Includer&) /fuzz/project/glslang/glslang/MachineIndependent/ShaderLang.cpp:1893:12
#17 0x5ca97397229c in glslang::TShader::parse(TBuiltInResource const*, int, EProfile, bool, bool, EShMessages) /fuzz/project/glslang/glslang/../glslang/Public/ShaderLang.h:675:16
#18 0x5ca9739720c2 in glslang::TShader::parse(TBuiltInResource const*, int, bool, EShMessages) /fuzz/project/glslang/glslang/../glslang/Public/ShaderLang.h:681:16
#19 0x5ca973971e5b in LLVMFuzzerTestOneInput /fuzz/project/compile_fuzzer.cc:28:10
#20 0x5ca973898193 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzz/fuzzers/compile_fuzzer+0x1ae193) (BuildId: d45de459a233d5e5e29d37b8c1e5522b70301d02)
#21 0x5ca973881f0f in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/fuzz/fuzzers/compile_fuzzer+0x197f0f) (BuildId: d45de459a233d5e5e29d37b8c1e5522b70301d02)
#22 0x5ca973887c66 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/fuzz/fuzzers/compile_fuzzer+0x19dc66) (BuildId: d45de459a233d5e5e29d37b8c1e5522b70301d02)
#23 0x5ca9738b1a82 in main (/fuzz/fuzzers/compile_fuzzer+0x1c7a82) (BuildId: d45de459a233d5e5e29d37b8c1e5522b70301d02)
#24 0x76df43986d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#25 0x76df43986e3f in __libc_start_main csu/../csu/libc-start.c:392:3
#26 0x5ca97387c7d4 in _start (/fuzz/fuzzers/compile_fuzzer+0x1927d4) (BuildId: d45de459a233d5e5e29d37b8c1e5522b70301d02)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /fuzz/project/glslang/glslang/MachineIndependent/Intermediate.cpp:541:19 in glslang::TIntermediate::isConversionAllowed(glslang::TOperator, glslang::TIntermTyped*) const
==5014==ABORTING
```
Contributor guide
Research direction
Start in glslang/MachineIndependent/Intermediate.cpp at TIntermediate::isConversionAllowed and inspect its caller addPairConversion, then trace the HLSL path shown in hlslGrammar.cpp. Reproduce the attached input with the provided AddressSanitizer fuzzer setup and determine the intended handling for a null TIntermTyped node; done means parsing no longer crashes with a null-pointer read.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100