From da90f374c12b0789d5b779401d3faae5871d321d Mon Sep 17 00:00:00 2001 From: Jan Dubois Date: Sun, 5 Apr 2026 21:01:50 -0700 Subject: [PATCH] Fix panic and OOM in repeatString for large repeat counts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing check (count > 10 million) does not account for string length. A 68-byte string repeated 35 trillion times passes the count check but panics in strings.Repeat with "makeslice: len out of range". Smaller counts (e.g. 10 million * 6-byte string = 60 MB) cause OOM on memory-constrained environments like OSS-Fuzz (2560 MB limit). Replace the count-only check with a result size check: the product of string length and repeat count must not exceed 10 MiB. Use division (len > limit/count) instead of multiplication (len*count > limit) to avoid integer overflow — a large count can wrap the product to a negative value, bypassing the guard entirely. Fixes at least four OSS-Fuzz bugs found via Lima's FuzzEvaluateExpression: https://issues.oss-fuzz.com/issues/418818862 (makeslice overflow) https://issues.oss-fuzz.com/issues/422001683 (timeout from huge alloc) https://issues.oss-fuzz.com/issues/383195001 (OOM, 3 GB allocation) https://issues.oss-fuzz.com/issues/385180606 (OOM, 97 TB allocation) Signed-off-by: Jan Dubois Co-Authored-By: Claude Opus 4.6 (1M context) --- pkg/yqlib/operator_multiply.go | 6 ++++-- pkg/yqlib/operator_multiply_test.go | 26 +++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/pkg/yqlib/operator_multiply.go b/pkg/yqlib/operator_multiply.go index b32ee3454c..9db2c4f5ab 100644 --- a/pkg/yqlib/operator_multiply.go +++ b/pkg/yqlib/operator_multiply.go @@ -155,8 +155,10 @@ func repeatString(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error return nil, err } else if count < 0 { return nil, fmt.Errorf("cannot repeat string by a negative number (%v)", count) - } else if count > 10000000 { - return nil, fmt.Errorf("cannot repeat string by more than 100 million (%v)", count) + } + maxResultLen := 10 * 1024 * 1024 // 10 MiB + if count > 0 && len(stringNode.Value) > maxResultLen/count { + return nil, fmt.Errorf("result of repeating string (%v bytes) by %v would exceed %v bytes", len(stringNode.Value), count, maxResultLen) } target.Value = strings.Repeat(stringNode.Value, count) diff --git a/pkg/yqlib/operator_multiply_test.go b/pkg/yqlib/operator_multiply_test.go index 7efeb52498..2f233eaf31 100644 --- a/pkg/yqlib/operator_multiply_test.go +++ b/pkg/yqlib/operator_multiply_test.go @@ -237,12 +237,11 @@ var multiplyOperatorScenarios = []expressionScenario{ expectedError: "cannot repeat string by a negative number (-4)", }, { - description: "Multiply string X by more than 100 million", - // very large string.repeats causes a panic + description: "Multiply string by count that exceeds result size limit", skipDoc: true, document: `n: 100000001`, expression: `"banana" * .n`, - expectedError: "cannot repeat string by more than 100 million (100000001)", + expectedError: "result of repeating string (6 bytes) by 100000001 would exceed 10485760 bytes", }, { description: "Multiply int node X string", @@ -693,6 +692,27 @@ var multiplyOperatorScenarios = []expressionScenario{ "D0, P[], (!!null)::null\n", }, }, + { + // Regression test for https://issues.oss-fuzz.com/issues/418818862 + // Large repeat count with a long string must not panic. + skipDoc: true, + expression: `"abc" * 99999999`, + expectedError: "result of repeating string (3 bytes) by 99999999 would exceed 10485760 bytes", + }, + { + // Regression test for https://issues.oss-fuzz.com/issues/383195001 + // Product of string length * repeat count must be bounded. + skipDoc: true, + expression: `"x" * 99999999`, + expectedError: "result of repeating string (1 bytes) by 99999999 would exceed 10485760 bytes", + }, + { + // The size guard must not overflow: len * count can wrap to + // a negative or small value on 64-bit, bypassing the check. + skipDoc: true, + expression: `"ab" * 4611686018427387904`, + expectedError: "result of repeating string (2 bytes) by 4611686018427387904 would exceed 10485760 bytes", + }, } func TestMultiplyOperatorScenarios(t *testing.T) {