From d7714ab4b88d45f309f03a969e8cc55800642f20 Mon Sep 17 00:00:00 2001 From: asteinh Date: Mon, 17 Aug 2026 17:45:20 +0200 Subject: [PATCH 1/2] fix: bounds-check tigris_mem_load_tile height window to fail closed on out-of-range rows --- src/tigris_mem.c | 8 +++++++ test/test_tile_2d.c | 51 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/src/tigris_mem.c b/src/tigris_mem.c index aa7930b..b2a8eb5 100644 --- a/src/tigris_mem.c +++ b/src/tigris_mem.c @@ -155,6 +155,14 @@ tigris_mem_error_t tigris_mem_load_tile( int32_t W = t->ndim == 4 ? shape[2] : 1; int32_t C = shape[t->ndim - 1]; + /* Fail closed on an out-of-range row window rather than reading past the + * source tensor. A correct tile plan always requests rows within [0, H]; a + * malformed or mis-tiled plan (e.g. a strided stage that loaded a + * post-spatial output-resolution operand at input rows) must not OOB-read. + * Mirrors the bounds guard in tigris_mem_load_tile_2d. */ + if (h_start < 0 || h_end <= h_start || h_end > H) + return TIGRIS_MEM_ERR_BAD_INDEX; + int32_t tile_h = h_end - h_start; uint32_t elem_size = t->size_bytes / (uint32_t)(N * H * W * C); uint32_t row_bytes = (uint32_t)W * (uint32_t)C * elem_size; diff --git a/test/test_tile_2d.c b/test/test_tile_2d.c index 4326860..f27f7b8 100644 --- a/test/test_tile_2d.c +++ b/test/test_tile_2d.c @@ -387,6 +387,56 @@ static void test_load_tile_2d_rejects_bad_bounds(void) TEST_ASSERT_EQ(e_w1_over, TIGRIS_MEM_ERR_BAD_INDEX, "out-of-range w1 rejected"); } +/* The 1D load primitive (tigris_mem_load_tile) must fail closed on an + * out-of-range height window exactly as the 2D primitive does, so a mis-tiled + * plan -- e.g. a strided stage that loads a post-spatial output-resolution + * operand at the spatial op's input rows -- returns an error instead of reading + * past the source tensor. */ +static void test_load_tile_1d_rejects_bad_bounds(void) +{ + printf(" test_load_tile_1d_rejects_bad_bounds...\n"); + + tigris_tensor_t tensor; + int32_t shape[4]; + tigris_plan_t plan; + t2d_build_plan(&tensor, shape, &plan); + + int8_t slow_buf[T2D_N * T2D_H * T2D_W * T2D_C]; + t2d_fill_source(slow_buf); + + uint8_t fast_buf[256]; + uint8_t slow_arena[128]; + void *tensor_ptrs[1] = { NULL }; + + tigris_mem_t mem; + tigris_mem_error_t merr = tigris_mem_init(&mem, tensor_ptrs, 1, + fast_buf, sizeof(fast_buf), + slow_arena, sizeof(slow_arena)); + TEST_ASSERT_EQ(merr, TIGRIS_MEM_OK, "mem init ok"); + mem.tensor_ptrs[T2D_TIDX] = slow_buf; + + /* A valid window ending exactly at H (the last-tile case) still loads: the + * guard must reject h_end > H, not h_end == H. */ + tigris_mem_error_t e_ok = tigris_mem_load_tile(&mem, &plan, T2D_TIDX, 4, T2D_H); + TEST_ASSERT_EQ(e_ok, TIGRIS_MEM_OK, "in-range 1D window (h_end == H) loads"); + mem.tensor_ptrs[T2D_TIDX] = slow_buf; /* restore slow base after the load */ + + /* h_start < 0 */ + tigris_mem_error_t e_neg = tigris_mem_load_tile(&mem, &plan, T2D_TIDX, -1, 6); + TEST_ASSERT_EQ(e_neg, TIGRIS_MEM_ERR_BAD_INDEX, "negative h_start rejected"); + mem.tensor_ptrs[T2D_TIDX] = slow_buf; + + /* h_end <= h_start, tested at equality */ + tigris_mem_error_t e_eq = tigris_mem_load_tile(&mem, &plan, T2D_TIDX, 3, 3); + TEST_ASSERT_EQ(e_eq, TIGRIS_MEM_ERR_BAD_INDEX, "h_end == h_start rejected"); + mem.tensor_ptrs[T2D_TIDX] = slow_buf; + + /* h_end > H: the post-spatial strided-skip mis-tile requests rows past the + * source tensor's height; must fail closed instead of reading OOB. */ + tigris_mem_error_t e_over = tigris_mem_load_tile(&mem, &plan, T2D_TIDX, 4, T2D_H + 4); + TEST_ASSERT_EQ(e_over, TIGRIS_MEM_ERR_BAD_INDEX, "out-of-range h_end rejected"); +} + static void test_spill_tile_2d_rejects_bad_bounds(void) { printf(" test_spill_tile_2d_rejects_bad_bounds...\n"); @@ -1565,6 +1615,7 @@ int main(void) test_load_tile_2d_roundtrip(); test_spill_tile_2d_roundtrip(); test_load_tile_2d_rejects_bad_bounds(); + test_load_tile_1d_rejects_bad_bounds(); test_spill_tile_2d_rejects_bad_bounds(); test_conv_2d_tile_matches_subregion_f32(); From 4992367202c8c14469001c038951db695c4201da Mon Sep 17 00:00:00 2001 From: asteinh Date: Mon, 17 Aug 2026 18:35:26 +0200 Subject: [PATCH 2/2] chore: update static-analysis baseline for load_tile bounds check --- scripts/static_analysis_baseline.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/static_analysis_baseline.json b/scripts/static_analysis_baseline.json index a382da3..1093880 100644 --- a/scripts/static_analysis_baseline.json +++ b/scripts/static_analysis_baseline.json @@ -18,10 +18,10 @@ "counts": {} }, "misra": { - "total": 2778, - "misra_total": 2751, + "total": 2782, + "misra_total": 2755, "config_errors": 2, - "fingerprint_sha256": "6f2784b2cc87208b4caaebc5c429a2bf11fdb359cc9d843c3cff3f808c9f509f", + "fingerprint_sha256": "732337ee2b5d804107de681074ae7b64602a6b39ccf61df1741750c2afa0e66a", "counts": { "include/tigris.h": { "misra-c2012-10.4": 4, @@ -158,10 +158,10 @@ "misra-c2012-10.8": 6, "misra-c2012-11.5": 10, "misra-c2012-11.6": 2, - "misra-c2012-12.1": 26, + "misra-c2012-12.1": 28, "misra-c2012-14.4": 1, - "misra-c2012-15.5": 50, - "misra-c2012-15.6": 51, + "misra-c2012-15.5": 51, + "misra-c2012-15.6": 52, "misra-c2012-17.7": 8, "misra-c2012-18.4": 17, "misra-c2012-8.7": 3