Skip to content

Commit 4505579

Browse files
committed
fix: throw on OOM shutdown instead of returning nullptr (#14)
getMemoryChunk() returned nullptr during hardShutdown but most callers never checked, causing null pointer dereferences under tight memory + heavy LOB redo. Now throws RuntimeException(10018) consistently. Validated on RAC VM: OLR shuts down gracefully instead of crashing.
1 parent 2603cd5 commit 4505579

5 files changed

Lines changed: 17 additions & 19 deletions

File tree

UPSTREAM-CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Multi-thread redo log processing for Oracle RAC.
3737
| 1a2d316b | LOB INSERT preserved from phantom undo in streaming | Transaction.cpp | [#10](https://github.com/rophy/olr/issues/10) |
3838
| ddf6bc04 | skip truncated URP null bitmap instead of abort | OpCode0504.cpp ||
3939
| 7cbd0580 | decode ROWID column type (type# 69) | Builder.cpp, SysCol.h, BuilderJson.* | [#15](https://github.com/rophy/olr/issues/15) |
40+
|| OOM null-deref crash: throw instead of returning nullptr | Ctx.cpp, MemoryManager.cpp | [#14](https://github.com/rophy/olr/issues/14) |
4041

4142
**Upstream PR candidates:** All — these are correctness fixes
4243

src/common/Ctx.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ namespace OpenLogReplicator {
459459
outOfMemoryParser = true;
460460

461461
if (hardShutdown)
462-
return nullptr;
462+
throw RuntimeException(10018, "shutdown during memory allocation for: " + memoryModules[static_cast<uint>(module)]);
463463

464464
info(0, "OOM: waiting for memory module=" + memoryModules[static_cast<uint>(module)] +
465465
" free=" + std::to_string(memoryChunksFree) + " alloc=" + std::to_string(memoryChunksAllocated) +

src/common/MemoryManager.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,6 @@ namespace OpenLogReplicator {
239239

240240
bool MemoryManager::unswap(Xid xid, int64_t index) {
241241
uint8_t* tc = ctx->getMemoryChunk(this, Ctx::MEMORY::TRANSACTIONS, true);
242-
if (tc == nullptr)
243-
return false;
244242

245243
const std::string fileName = swapPath + "/" + xid.toString() + ".swap";
246244
struct stat fileStat{};

tests/KNOWN-LIMITATIONS.md

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -230,26 +230,25 @@ no output for the scenario.
230230

231231
---
232232

233-
## L10. OLR Crash on RAC LOB + Log Switch
233+
## L10. ~~OLR Crash on RAC LOB + Log Switch~~ (FIXED)
234234

235-
OLR crashes with a null pointer dereference in `Reader.cpp:111` when
236-
processing heavy LOB operations spanning multiple log switches on RAC.
235+
**Root cause:** `Ctx::getMemoryChunk()` returned `nullptr` during
236+
`hardShutdown` (OOM condition), but 7 of 9 callers never checked for null.
237+
Under tight memory + heavy LOB redo, the parser consumed all available chunks,
238+
the reader's allocation failed silently, and the subsequent dereference
239+
crashed.
237240

238-
**Evidence — test output (2026-03-16):**
241+
**Fix:** Changed `getMemoryChunk()` to throw `RuntimeException(10018)` instead
242+
of returning `nullptr` on hard shutdown. This is consistent with the existing
243+
throw at line 489-490 (post-allocation shutdown check). Removed the one
244+
now-unreachable null check in `MemoryManager::unswap()`.
239245

240-
```
241-
Reader.cpp:111:21: runtime error: load of null pointer of type 'uint8_t'
242-
```
243-
244-
Occurs when advancing to a new archive log sequence on thread 2 during
245-
`rac-lob-log-switch` scenario (40 LOB inserts + 10 updates + 10 deletes
246-
across both nodes).
246+
**Validated:** Reproduced 10/10 on RAC VM with 32-64MB memory + LOB DML from
247+
both nodes. After fix, OLR shuts down gracefully with error 10018 instead of
248+
crashing.
247249

248250
**Tracked:** [rophy/olr#14](https://github.com/rophy/olr/issues/14)
249251

250-
**Test handling:** `rac-lob-log-switch` scenario cannot be used until bug is
251-
fixed.
252-
253252
---
254253

255254
## L11. OLR Does Not Support Invisible Columns
@@ -305,6 +304,6 @@ applies at DB creation, not pre-built).
305304
|----|------------|-------|---------------|
306305
| L8 | ROWID column (type# 69) not decoded | [#15](https://github.com/rophy/olr/issues/15) | `rowid-column` |
307306
| L9 | IOT not discovered in metadata | [#16](https://github.com/rophy/olr/issues/16) | `iot-table` |
308-
| L10 | RAC LOB + log switch null pointer crash | [#14](https://github.com/rophy/olr/issues/14) | `rac-lob-log-switch` |
307+
| ~~L10~~ | ~~RAC LOB + log switch null pointer crash~~ (FIXED) | [#14](https://github.com/rophy/olr/issues/14) | |
309308
| L11 | Invisible columns not tracked |||
310309
| L12 | US7ASCII charset corruption | [#2](https://github.com/rophy/olr/issues/2) | `multibyte-passthrough` (@TAG) |

tests/sql/TEST-COVERAGE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ All limitations reference entries in [`KNOWN-LIMITATIONS.md`](KNOWN-LIMITATIONS.
9191
| US7ASCII charset | OLR charset bug ([#2](https://github.com/rophy/olr/issues/2)) | L12 |
9292
| Invisible columns | No property flag in SysCol | L11 |
9393
| IOT | OLR doesn't discover IOTs in metadata | L9 |
94-
| RAC LOB + log switch | OLR crash ([#14](https://github.com/rophy/olr/issues/14)) | L10 |
94+
| ~~RAC LOB + log switch~~ | ~~OLR crash~~ (FIXED — [#14](https://github.com/rophy/olr/issues/14)) | ~~L10~~ |
9595

9696
> **Note:** DDL scenarios (`@DDL` marker) are validated in redo-log regression tests (LogMiner comparison)
9797
> but **not** in Debezium twin-test. The Debezium OLR adapter does not support mid-stream

0 commit comments

Comments
 (0)