diff --git a/src/include/firebird/impl/msg/jrd.h b/src/include/firebird/impl/msg/jrd.h index e3c020d60f9..340fd1fafdb 100644 --- a/src/include/firebird/impl/msg/jrd.h +++ b/src/include/firebird/impl/msg/jrd.h @@ -1015,3 +1015,4 @@ FB_IMPL_MSG(JRD, 1012, bad_constant_type, -901, "2F", "000", "@1 is not supporte FB_IMPL_MSG(JRD, 1013, not_defined_constant, -901, "42", "000", "The constant @1 is not defined") FB_IMPL_MSG(JRD, 1014, const_name, -901, "42", "000", "CONSTANT @1") FB_IMPL_MSG(JRD, 1015, private_table, -901, "42", "000", "Table @1 is private to package @2") +FB_IMPL_MSG(JRD, 1016, temp_space_invalid_pos, -901, "HY", "000", "Invalid position to read/write in a temporary file (positon: @1, size: @2)") diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index 2b3b1c97065..5303fde2004 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -5972,6 +5972,7 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_not_defined_constant = 335545333; isc_const_name = 335545334; isc_private_table = 335545335; + isc_temp_space_invalid_pos = 335545336; isc_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; isc_gfix_incmp_sw = 335740932; diff --git a/src/jrd/TempSpace.cpp b/src/jrd/TempSpace.cpp index 6e22ecfd869..3f7ec355933 100644 --- a/src/jrd/TempSpace.cpp +++ b/src/jrd/TempSpace.cpp @@ -258,7 +258,11 @@ TempSpace::~TempSpace() FB_SIZE_T TempSpace::read(offset_t offset, void* buffer, FB_SIZE_T length) { - fb_assert(offset + length <= logicalSize); + if (offset + length > logicalSize) + { + status_exception::raise(Arg::Gds(isc_temp_space_invalid_pos) << + Arg::Int64(offset + length) << Arg::Int64(logicalSize)); + } if (length) { @@ -290,7 +294,11 @@ FB_SIZE_T TempSpace::read(offset_t offset, void* buffer, FB_SIZE_T length) FB_SIZE_T TempSpace::write(offset_t offset, const void* buffer, FB_SIZE_T length) { - fb_assert(offset <= logicalSize); + if (offset > logicalSize) + { + status_exception::raise(Arg::Gds(isc_temp_space_invalid_pos) << + Arg::Int64(offset) << Arg::Int64(logicalSize)); + } if (offset + length > logicalSize) { @@ -328,9 +336,17 @@ FB_SIZE_T TempSpace::write(offset_t offset, const void* buffer, FB_SIZE_T length void TempSpace::extend(FB_SIZE_T size) { + const auto originalLogicalSize = logicalSize; + const auto originalPhysicalSize = physicalSize; + + AutoPtr originalHead; // Delay deletion and restore in case of error + Block* originalTail = tail; + logicalSize += size; + if (logicalSize <= physicalSize) + return; - if (logicalSize > physicalSize) + try { const FB_SIZE_T initialSize = initialBuffer.getCount(); @@ -367,7 +383,7 @@ void TempSpace::extend(FB_SIZE_T size) if (initialSize) { fb_assert(head == tail); - delete head; + originalHead = head; head = tail = NULL; size = static_cast(FB_ALIGN(logicalSize, minBlockSize)); physicalSize = size; @@ -399,12 +415,10 @@ void TempSpace::extend(FB_SIZE_T size) } } - // NS 2014-07-31: FIXME: missing exception handling. - // error thrown in block of code below will leave TempSpace in inconsistent state: - // logical/physical size already increased while allocation has in fact failed. if (!block) { // allocate block in the temp file + // Possible error thrown when not enough physical memory TempFile* const file = setupFile(size); fb_assert(file); if (tail && tail->sameFile(file)) @@ -430,6 +444,15 @@ void TempSpace::extend(FB_SIZE_T size) } tail = block; } + catch (...) + { + // Restore original state + logicalSize = originalLogicalSize; + physicalSize = originalPhysicalSize; + head = originalHead.release(); + tail = originalTail; + throw; + } } //