Commit 4180722ee for llama.cpp

commit 4180722eeb3b0335700edb0b1e195d5ca05d0191
Author: CC <65151926+realMoana@users.noreply.github.com>
Date:   Wed Nov 11 15:02:06 2026 -0500

    metal : prevent out-of-bounds reads in SOLVE_TRI kernel (#32103)

diff --git a/ggml/src/ggml-metal/kernels/solve_tri.metal b/ggml/src/ggml-metal/kernels/solve_tri.metal
index 50f16facb..216f605fe 100644
--- a/ggml/src/ggml-metal/kernels/solve_tri.metal
+++ b/ggml/src/ggml-metal/kernels/solve_tri.metal
@@ -39,7 +39,9 @@ kernel void kernel_solve_tri_f32(

             for (short t = 0; t*NW < N; ++t) {
                 const short idx = t*NW + tiisg;
-                sh0_cur[idx] = src0_ptr[idx];
+                if (rr + sgitg < N && idx < N) {
+                    sh0_cur[idx] = src0_ptr[idx];
+                }
             }

             src0_ptr += NSG*N;
@@ -60,7 +62,9 @@ kernel void kernel_solve_tri_f32(

             for (short t = 0; t*NW < r; ++t) {
                 const short idx = t*NW + tiisg;
-                sum += sh0_cur[idx] * dst_ptr[idx*K] * (idx < r);
+                if (idx < r) {
+                    sum += sh0_cur[idx] * dst_ptr[idx*K];
+                }
             }

             sum = simd_sum(sum);
diff --git a/tests/test-backend-ops.cpp b/tests/test-backend-ops.cpp
index ce2bdf0bb..a63d5e24d 100644
--- a/tests/test-backend-ops.cpp
+++ b/tests/test-backend-ops.cpp
@@ -9592,8 +9592,9 @@ struct test_solve_tri : public test_case {
     const ggml_type              type;
     const std::array<int64_t, 4> ne_lhs;
     const std::array<int64_t, 4> ne_rhs;
+    const bool                   nan_out; // fill the output with NaN before the op runs

-    std::string vars() override { return VARS_TO_STR3(type, ne_lhs, ne_rhs); }
+    std::string vars() override { return VARS_TO_STR4(type, ne_lhs, ne_rhs, nan_out); }

     uint64_t op_flops(ggml_tensor * t) override {
         GGML_UNUSED(t);
@@ -9606,9 +9607,10 @@ struct test_solve_tri : public test_case {

     test_solve_tri(ggml_type type = GGML_TYPE_F32,
             std::array<int64_t, 4> ne_lhs = { 10, 10, 4, 3 },
-            std::array<int64_t, 4> ne_rhs = { 3, 10, 4, 3 }
+            std::array<int64_t, 4> ne_rhs = { 3, 10, 4, 3 },
+            bool nan_out = false
         )
-        : type(type), ne_lhs(ne_lhs), ne_rhs(ne_rhs) {}
+        : type(type), ne_lhs(ne_lhs), ne_rhs(ne_rhs), nan_out(nan_out) {}

     ggml_tensor * build_graph(ggml_context * ctx) override {
         ggml_tensor * a = ggml_new_tensor_4d(ctx, type, ne_lhs[0], ne_lhs[1], ne_lhs[2], ne_lhs[3]);
@@ -9630,6 +9632,9 @@ struct test_solve_tri : public test_case {
             if (strcmp(t->name, "a") == 0) {
                 // note: avoid zeros in the diagonal
                 init_tensor_tril(t, 0.1, 1.0f);
+            } else if (nan_out && strcmp(t->name, "out") == 0) {
+                std::vector<float> data(ggml_nelements(t), NAN);
+                ggml_backend_tensor_set(t, data.data(), 0, ggml_nbytes(t));
             } else {
                 init_tensor_uniform(t, -1.0f, 1.0f);
             }
@@ -12854,6 +12859,9 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
     test_cases.emplace_back(new test_solve_tri(GGML_TYPE_F32, { 128, 128, 4, 1 }, { 32, 128, 4, 1 }));
     test_cases.emplace_back(new test_solve_tri(GGML_TYPE_F32, { 64, 64, 4, 4 }, { 200, 64, 4, 4 }));
     test_cases.emplace_back(new test_solve_tri(GGML_TYPE_F32, { 64, 64, 4, 4 }, { 384, 64, 4, 4 }));
+    // n % 32 != 0, output prefilled with NaN
+    test_cases.emplace_back(new test_solve_tri(GGML_TYPE_F32, { 34, 34, 1, 1 }, { 256, 34, 1, 1 }, true));
+    test_cases.emplace_back(new test_solve_tri(GGML_TYPE_F32, { 79, 79, 5, 3 }, { 417, 79, 5, 3 }, true));

     for (int tfrm : {0, 1, 2}) {
         for (bool circular : {false, true}) {