| # orc-encoding-kind-nullwrite-poc | |
| PoC `.orc` for a native crash in Apache Arrow's ORC C++ reader (pyarrow 25.0.0, latest), hit by `pyarrow.orc.read_table` on the file β one corrupted byte in the stripe footer. | |
| the byte is a `ColumnEncoding.kind` inside the stripe footer (`StripeFooter.columns[i].kind`, protobuf field 1). set it to an out-of-range enum value (`27` instead of `2` = `DIRECT_V2`) and the column reader picks the wrong RLE/decode path, computes an underflowed element count (~`0xffffβ¦`, i.e. a huge uint64), and passes it straight to `orc::ColumnVectorBatch::resize` with no bound. `orc::DataBuffer<char>::resize` `malloc`s that ~1.8e19-byte size, gets `NULL`, and `memset`s into the NULL dest β write to address 0 β SIGSEGV. not a clean `ArrowInvalid`, not a `bad_alloc` that unwinds β a hard native crash. | |
| ## files | |
| - `orc_min.orc` β the crafted file (13251 bytes). `pyarrow.orc.read_table("orc_min.orc")` β SIGSEGV. | |
| - `orc_encoding_kind_poc.py` β deterministic generator: builds a rich 14-column table, finds a `DIRECT_V2` encoding in the stripe footer, flips the `kind` byte to 27, verifies the crash in a fresh subprocess. | |
| - `read_orc.py` β the plain read path (`orc.read_table(argv[1])`), run per-file in its own process. | |
| ## repro | |
| pip install pyarrow==25.0.0 | |
| python orc_encoding_kind_poc.py # -> flips kind at offset 11938, writes orc_min.orc, rc=-11 | |
| python read_orc.py orc_min.orc ; echo $? # -> 139 (128 + SIGSEGV) | |
| it's a reliable NULL-pointer write / DoS on load of an untrusted `.orc`, not a controllable OOB write (the underflowed size is too big to allocate β malloc NULL β fault at 0). no RCE claimed. single byte, no compression, ordinary columns. | |