From a62081d79e02873a2f278a67894ad71f0b78cd80 Mon Sep 17 00:00:00 2001 From: frosch Date: Mon, 12 May 2025 13:59:33 +0200 Subject: [PATCH] Fix #14251: Out-of-bounds writes via TextRefStack. (#14255) --- src/newgrf_text.cpp | 57 ++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/src/newgrf_text.cpp b/src/newgrf_text.cpp index 7394b49629..86d4b24a39 100644 --- a/src/newgrf_text.cpp +++ b/src/newgrf_text.cpp @@ -30,6 +30,7 @@ #include "core/string_builder.hpp" #include "core/string_consumer.hpp" #include "language.h" +#include #include "table/strings.h" #include "table/control_codes.h" @@ -676,21 +677,26 @@ void CleanUpStrings() } struct TextRefStack { - std::array stack{}; - uint8_t position = 0; +private: + std::vector stack; +public: const GRFFile *grffile = nullptr; TextRefStack(const GRFFile *grffile, std::span textstack) : grffile(grffile) { - auto stack_it = this->stack.begin(); - for (int32_t value : textstack) { - for (uint j = 0; j < 32; j += 8) { - *stack_it++ = GB(value, j, 8); - } + this->stack.reserve(textstack.size() * 4 + 6); // for translations it is reasonable to push 3 word and rotate them + for (int32_t value : std::ranges::reverse_view(textstack)) { + this->PushDWord(value); } } - uint8_t PopUnsignedByte() { assert(this->position < this->stack.size()); return this->stack[this->position++]; } + uint8_t PopUnsignedByte() + { + if (this->stack.empty()) return 0; + auto res = this->stack.back(); + this->stack.pop_back(); + return res; + } int8_t PopSignedByte() { return (int8_t)this->PopUnsignedByte(); } uint16_t PopUnsignedWord() @@ -717,22 +723,31 @@ struct TextRefStack { /** Rotate the top four words down: W1, W2, W3, W4 -> W4, W1, W2, W3 */ void RotateTop4Words() { - uint8_t tmp[2]; - for (int i = 0; i < 2; i++) tmp[i] = this->stack[this->position + i + 6]; - for (int i = 5; i >= 0; i--) this->stack[this->position + i + 2] = this->stack[this->position + i]; - for (int i = 0; i < 2; i++) this->stack[this->position + i] = tmp[i]; + auto w1 = this->PopUnsignedWord(); + auto w2 = this->PopUnsignedWord(); + auto w3 = this->PopUnsignedWord(); + auto w4 = this->PopUnsignedWord(); + this->PushWord(w3); + this->PushWord(w2); + this->PushWord(w1); + this->PushWord(w4); } - void PushWord(uint16_t word) + void PushByte(uint8_t b) { - if (this->position >= 2) { - this->position -= 2; - } else { - /* Rotate right 2 positions */ - std::rotate(this->stack.rbegin(), this->stack.rbegin() + 2, this->stack.rend()); - } - this->stack[this->position] = GB(word, 0, 8); - this->stack[this->position + 1] = GB(word, 8, 8); + this->stack.push_back(b); + } + + void PushWord(uint16_t w) + { + this->PushByte(GB(w, 8, 8)); + this->PushByte(GB(w, 0, 8)); + } + + void PushDWord(uint32_t dw) + { + this->PushWord(GB(dw, 16, 16)); + this->PushWord(GB(dw, 0, 16)); } };