diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8621bf92f..906da589d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -47,6 +47,7 @@ Other improvements ------------------ - A bug that prevented certain executables from being offered in tab-completions when root has been fixed (:issue:`9639`). - Builin `jobs` will print commands with non-printable chars escaped (:issue:`9808`) +- An integer overflow in `string repeat` leading to a near-infinite loop has been fixed (:issue:`9899`). For distributors ---------------- diff --git a/src/builtins/string.cpp b/src/builtins/string.cpp index 1a5695575..6b7896938 100644 --- a/src/builtins/string.cpp +++ b/src/builtins/string.cpp @@ -1540,7 +1540,7 @@ static int string_repeat(parser_t &parser, io_streams_t &streams, int argc, cons wcstring chunk; chunk.reserve(std::min(chunk_size + w.length(), max)); - for (size_t i = max; i > 0; i -= w.length()) { + for (size_t i = max; i > 0;) { // Build up the chunk. if (i >= w.length()) { chunk.append(w); @@ -1548,6 +1548,9 @@ static int string_repeat(parser_t &parser, io_streams_t &streams, int argc, cons chunk.append(w.substr(0, i)); break; } + + i -= w.length(); + if (chunk.length() >= chunk_size) { // We hit the chunk size, write it repeatedly until we can't anymore. streams.out.append(chunk); diff --git a/tests/checks/string.fish b/tests/checks/string.fish index 52dcec924..8ffeef150 100644 --- a/tests/checks/string.fish +++ b/tests/checks/string.fish @@ -530,6 +530,10 @@ string repeat -n 17 a | string length string repeat -m 5 (string repeat -n 500000 aaaaaaaaaaaaaaaaaa) | string length # CHECK: 5 +# might cause integer overflow +string repeat -n 2999 \n | count +# CHECK: 3000 + # Test equivalent matches with/without the --entire, --regex, and --invert flags. string match -e x abc dxf xyz jkx x z or echo exit 1