diff --git a/c++/src/Timezone.cc b/c++/src/Timezone.cc index 6c3f58c079..b9499bd6d0 100644 --- a/c++/src/Timezone.cc +++ b/c++/src/Timezone.cc @@ -773,7 +773,12 @@ namespace orc { std::string newfilename(dir); newfilename += "/"; newfilename += it->second; - timezoneCache[newfilename] = std::make_shared(newfilename); + // Only insert if absent: overwriting an existing entry destroys the + // LazyTimezone while raw pointers in live TimestampColumnReaders still + // reference it, causing a use-after-free (SIGSEGV) on the next read. + if (timezoneCache.find(newfilename) == timezoneCache.end()) { + timezoneCache[newfilename] = std::make_shared(newfilename); + } timezoneCache[filename] = timezoneCache[newfilename]; } return *timezoneCache[filename].get(); diff --git a/c++/test/TestWriter.cc b/c++/test/TestWriter.cc index 5827bcdbca..764ead1c85 100644 --- a/c++/test/TestWriter.cc +++ b/c++/test/TestWriter.cc @@ -2648,6 +2648,47 @@ namespace orc { } } + TEST(TimestampAliasCacheEviction, readerSurvivesAliasCacheEviction) { + MemoryOutputStream memStream(DEFAULT_MEM_STREAM_SIZE); + MemoryPool* pool = getDefaultPool(); + std::unique_ptr type(Type::buildTypeFromString("struct")); + + // Write 2000 rows so that reading with a 1024-row batch requires two next() calls. + const uint64_t rowCount = 2000; + std::unique_ptr writer = + createWriter(64 * 1024 * 1024, 64 * 1024, 64 * 1024, CompressionKind_ZLIB, *type, pool, + &memStream, FileVersion::v_0_12(), 0, "America/New_York"); + std::unique_ptr batch = writer->createRowBatch(rowCount); + StructVectorBatch* structBatch = dynamic_cast(batch.get()); + TimestampVectorBatch* tsBatch = dynamic_cast(structBatch->fields[0]); + for (uint64_t i = 0; i < rowCount; ++i) { + tsBatch->data[i] = static_cast(i * 3600); + tsBatch->nanoseconds[i] = 0; + } + structBatch->numElements = rowCount; + tsBatch->numElements = rowCount; + writer->add(*batch); + writer->close(); + + auto inStream = std::make_unique(memStream.getData(), memStream.getLength()); + std::unique_ptr reader = createReader(pool, std::move(inStream)); + std::unique_ptr rowReader = createRowReader(reader.get(), "GMT"); + ASSERT_EQ(rowCount, reader->getNumberOfRows()); + + std::unique_ptr readBatch = rowReader->createRowBatch(1024); + + // First next() opens the stripe, constructs TimestampColumnReader, and stores + // writerTimezone_ = &getTimezoneByName("America/New_York"). + ASSERT_TRUE(rowReader->next(*readBatch)); + ASSERT_EQ(1024u, readBatch->numElements); + + // Populate aliases in the timezone cache + (void)getTimezoneByName("US/Eastern"); + + // Verify that writerTimezone_ is still a valid pointer + EXPECT_TRUE(rowReader->next(*readBatch)); + } + std::vector testParams = {{FileVersion::v_0_11(), true}, {FileVersion::v_0_11(), false}, {FileVersion::v_0_12(), false},