CesiumGS / CesiumGS/cesium-native

PntsToGltfConverter.cpp OOB read

Open Beginner friendly
#1,425 1 comment 0 reactions 0 assignees View on GitHub
bug good first issue
Dominant language
C++
Stars
623
Forks
277
PR merge metrics
No merged PRs in 30d

Description

## 1. Vulnerability Description
The heap-buffer-overflow READ described in the report is reproducible on the exact commit (004a56acdd0bd9c5c1f6142dc5c1264c639d5a83) cited. A 28-byte malicious .pnts file triggers an out-of-bounds read in PntsToGltfConverter.cpp, detected by AddressSanitizer as a heap-buffer-overflow of size 1 at the byte immediately following the 28-byte allocation.

## 2. Vulnerability Analysis
`parsePntsHeader` (line 84–114) performs only **one** size check on the incoming tile buffer:

```cpp
// PntsToGltfConverter.cpp:108
if (static_cast(pntsBinary.size()) < pHeader->byteLength) {
result.errors.emplaceError(
"The PNTS is invalid because the total data available is less than the "
"size specified in its header.");
return;
}
```
This check verifies that the **outer buffer is at least `byteLength` bytes**, but it does **not** verify that the four declared section lengths actually fit inside `byteLength`:

```
headerLength + featureTableJsonByteLength
+ featureTableBinaryByteLength
+ batchTableJsonByteLength
+ batchTableBinaryByteLength <= byteLength <-- NEVER CHECKED
```

Consequently, when `convertPntsContentToGltf` (line 1531) builds the feature table JSON span:

```cpp
// PntsToGltfConverter.cpp:1540-1541
const std::span featureTableJsonData =
pntsBinary.subspan(headerLength, header.featureTableJsonByteLength);
```

`std::span::subspan` performs no bounds check in release mode. If `headerLength + featureTableJsonByteLength > pntsBinary.size()`, the resulting span points past the end of the allocation.

That span is then handed to `parseFeatureTableJson` (line 556), which calls `rapidjson::Document::Parse(ptr, len)`. RapidJSON's `MemoryStream::Peek()` reads the first byte at `ptr` — which is now an out-of-bounds heap address — triggering the heap-buffer-overflow READ.

A second, structurally identical vulnerable path exists for the batch table JSON at lines 1553–1559 (`batchTableStart` + `subspan`).

## 3. Vulnerability Reproduction

[poc_pnts_oob.zip](https://github.com/user-attachments/files/30246331/poc_pnts_oob.zip)

Reproduction command:
```bash
g++ -std=c++20 -O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer \
-I/home/work/rapidjson/include \
/home/work/scripts/poc_pnts_oob.cpp -o /tmp/poc && \
ASAN_OPTIONS=detect_leaks=0 /tmp/poc /home/download/poc_pnts_oob.pnts
```

## 4、Additional Vulnerable Path

The same missing-check pattern affects the **batch table JSON** path at lines 1553–1559:

```cpp
// PntsToGltfConverter.cpp:1553-1559
const int64_t batchTableStart = headerLength +
header.featureTableJsonByteLength +
header.featureTableBinaryByteLength;
rapidjson::Document batchTableJson;
if (header.batchTableJsonByteLength > 0) {
const std::span batchTableJsonData = pntsBinary.subspan(
static_cast(batchTableStart),
header.batchTableJsonByteLength); // <-- also unchecked
```

A PNTS with `byteLength=28, ftJson=1, ftBin=1, btJson=1` (file `poc_pnts_oob_batchtable.pnts`) reaches this path. In the probe it crashes at the feature-table subspan first (which is encountered earlier in the same function), but in the real library the same class of OOB read applies to the batch table as well. A proper fix must cover both paths.

## 5、Recommended Fix
Add a single bounds check in `parsePntsHeader`, immediately after the existing `byteLength` check (after line 113):

```cpp
const uint64_t headerLength64 = sizeof(PntsHeader);
const uint64_t sectionsEnd =
headerLength64 +
header.featureTableJsonByteLength +
header.featureTableBinaryByteLength +
header.batchTableJsonByteLength +
header.batchTableBinaryByteLength;

if (sectionsEnd > header.byteLength) {
result.errors.emplaceError(
"The PNTS is invalid because the feature/batch table sections extend "
"past the byteLength declared in the header.");
return;
}
```

Using `uint64_t` for the intermediate sum avoids `uint32_t` overflow in the addition. This single check protects both the feature-table and batch-table `subspan` calls downstream.

Contributor guide

Open the contributing guide

Research direction

Start in PntsToGltfConverter.cpp by reading parsePntsHeader and convertPntsContentToGltf, including the feature-table and batch-table subspan paths. Reproduce the issue with the provided AddressSanitizer command and poc_pnts_oob files. Done means malformed section lengths are rejected before either subspan can read past byteLength, covering both JSON paths.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
computer-graphics, security
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.