From 21e109b32422ebe35e137849759a5008668ea29f Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Wed, 14 Oct 2026 18:37:36 +0000 Subject: [PATCH] patch 9.2.1187: tuple type with many items reads past the end of an array Problem: A tuple type with more than 127 items gets a wrong item count and reads past the end of an array. Allocation failure for a tuple type is not handled. Solution: Use an int for the item count. Use the "any" type when allocation fails (Hirohito Higashi). closes: #21517 Assisted-by: Claude Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- src/alloc.h | 1 + src/structs.h | 4 ++-- src/testdir/test_tuple.vim | 32 ++++++++++++++++++++++++++++++++ src/version.c | 2 ++ src/vim9instr.c | 3 ++- src/vim9type.c | 12 +++++++----- 6 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/alloc.h b/src/alloc.h index 3d740bca3e..29495a186c 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -47,5 +47,6 @@ typedef enum { aid_get_func, aid_defer, aid_tuple_append, + aid_tuple_type_args, aid_last } alloc_id_T; diff --git a/src/structs.h b/src/structs.h index 5f61427989..eb38f2526a 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1640,8 +1640,8 @@ typedef enum // A type specification. struct type_S { vartype_T tt_type; - int8_T tt_argcount; // for func, incl. vararg, -1 for unknown - int8_T tt_min_argcount; // number of non-optional arguments + int tt_argcount; // for func, incl. vararg, -1 for unknown + int tt_min_argcount; // number of non-optional arguments short_u tt_flags; // TTFLAG_ values type_T *tt_member; // for list, dict, func return type class_T *tt_class; // for class and object diff --git a/src/testdir/test_tuple.vim b/src/testdir/test_tuple.vim index d9011c6e76..a6c0b64675 100644 --- a/src/testdir/test_tuple.vim +++ b/src/testdir/test_tuple.vim @@ -2542,4 +2542,36 @@ func Test_tuple_unpack_memory_fail() unlet t endfunc +" Memory allocation failure while creating a tuple type +func Test_tuple_type_memory_fail() + let lines =<< trim END + var t = (1, 'a') + END + call test_alloc_fail(GetAllocId('tuple_type_args'), 0, 0) + call v9.CheckSourceDefFailure(lines, 'E342:') + + let lines =<< trim END + var t = (1,) + ('a',) + END + call test_alloc_fail(GetAllocId('tuple_type_args'), 2, 0) + call v9.CheckSourceDefFailure(lines, 'E342:') +endfunc + +" Test for a tuple type with more than 127 items +func Test_tuple_type_many_items() + let types = repeat(['number'], 300)->join(', ') + let lines =<< trim eval END + var t: tuple<{types}> = ({range(300)->join(', ')}) + assert_equal(299, t[299]) + assert_equal('tuple<{types}>', typename(t)) + END + call v9.CheckSourceDefAndScriptSuccess(lines) + + let types = repeat(['number'], 200)->join(', ') + let lines =<< trim eval END + var t: tuple<{types}> = ({repeat(["'x'"], 200)->join(', ')}) + END + call v9.CheckSourceDefAndScriptFailure(lines, 'E1012:', 1) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 8f79078766..251c01b130 100644 --- a/src/version.c +++ b/src/version.c @@ -743,6 +743,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1187, /**/ 1186, /**/ diff --git a/src/vim9instr.c b/src/vim9instr.c index a3e9243cac..5bee4087e8 100644 --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -317,7 +317,8 @@ set_tuple_union_type_on_stack(type_T *type1, type_T *type2, cctx_T *cctx) type_T *new_tuple_type = get_tuple_type(&tuple_types_ga, cctx->ctx_type_list); // result inherits the variadic flag from the operands - new_tuple_type->tt_flags |= (type1->tt_flags & TTFLAG_VARARGS) + if ((new_tuple_type->tt_flags & TTFLAG_STATIC) == 0) + new_tuple_type->tt_flags |= (type1->tt_flags & TTFLAG_VARARGS) | (type2->tt_flags & TTFLAG_VARARGS); // set the type on the stack for the resulting tuple diff --git a/src/vim9type.c b/src/vim9type.c index ed633b4eeb..14a2448ff1 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -321,7 +321,8 @@ tuple_type_add_types( // pointer to type_gap. if (ga_grow(type_gap, 1) == FAIL) return FAIL; - tupletype->tt_args = ALLOC_CLEAR_MULT(type_T *, typecount); + tupletype->tt_args = alloc_clear_id(sizeof(type_T *) * typecount, + aid_tuple_type_args); if (tupletype->tt_args == NULL) return FAIL; ((type_T **)type_gap->ga_data)[type_gap->ga_len] = @@ -389,7 +390,7 @@ get_tuple_type(garray_T *tuple_types_gap, garray_T *type_gap) if (typecount > 0) { if (tuple_type_add_types(type, typecount, type_gap) == FAIL) - return NULL; + return &t_any; mch_memmove(type->tt_args, tuple_types, sizeof(type_T *) * typecount); } type->tt_argcount = typecount; @@ -1919,13 +1920,14 @@ parse_type_tuple( *arg = p + 1; ret_type = alloc_tuple_type(typecount, type_gap); - ret_type->tt_flags = flags; - ret_type->tt_argcount = typecount; - if (tuple_type_add_types(ret_type, typecount, type_gap) == FAIL) + if (ret_type == &t_any + || tuple_type_add_types(ret_type, typecount, type_gap) == FAIL) { ret_type = NULL; goto on_err; } + ret_type->tt_flags = flags; + ret_type->tt_argcount = typecount; mch_memmove(ret_type->tt_args, tuple_types_ga.ga_data, sizeof(type_T *) * typecount);