[llvm] [Offload] Avoid writing past the destination in host dataFill (PR #223331)

via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 14 02:00:21 PDT 2026


https://github.com/StevenYangCC created https://github.com/llvm/llvm-project/pull/223331

## Summary
- The host plugin copied `PatternSize` bytes on every `dataFill` iteration, so a fill size that was not a multiple of the pattern size overran the destination. A pattern size of zero also never advanced the offset.
- Clamp each copy to the remaining destination bytes and treat a non-positive pattern or fill size as a no-op.
- Share that fill helper with unit tests for remainder, zero-size, and single-byte cases.

## Test plan
- [ ] `ninja memory.unittests` then `./offload/unittests/OffloadAPI/memory.unittests --gtest_filter='HostPatternFill.*'`
- [ ] Confirm `RemainderDoesNotWritePastSize` leaves the canary bytes intact
- [ ] Confirm `ZeroPatternSizeIsNoOp` returns without hanging
- [ ] Existing `olMemFillTest.*` still pass (API validation of non-multiples is unchanged)

>From 25719f9a6276ef36892d20702e67f8c12ab9c40a Mon Sep 17 00:00:00 2001
From: "chengcang.yang" <yangchengcang at gmail.com>
Date: Mon, 14 Sep 2026 16:51:01 +0800
Subject: [PATCH] [Offload] Avoid writing past the destination in host dataFill

The host plugin copied PatternSize bytes on every loop iteration, so a
fill size that was not a multiple of the pattern size overran the
buffer. A pattern size of zero also never advanced the offset.

Clamp each copy to the remaining destination bytes and treat a
non-positive pattern or fill size as a no-op. Add unit tests for the
remainder and zero-size cases.
---
 .../host/src/HostPatternFill.h                | 51 +++++++++++++
 offload/plugins-nextgen/host/src/rtl.cpp      | 11 +--
 offload/unittests/OffloadAPI/CMakeLists.txt   |  3 +
 .../OffloadAPI/memory/HostPatternFillTest.cpp | 72 +++++++++++++++++++
 4 files changed, 128 insertions(+), 9 deletions(-)
 create mode 100644 offload/plugins-nextgen/host/src/HostPatternFill.h
 create mode 100644 offload/unittests/OffloadAPI/memory/HostPatternFillTest.cpp

diff --git a/offload/plugins-nextgen/host/src/HostPatternFill.h b/offload/plugins-nextgen/host/src/HostPatternFill.h
new file mode 100644
index 00000000000000..9df00c45f8ea98
--- /dev/null
+++ b/offload/plugins-nextgen/host/src/HostPatternFill.h
@@ -0,0 +1,51 @@
+//===- HostPatternFill.h - Host plugin pattern fill -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef OFFLOAD_PLUGINS_NEXTGEN_HOST_SRC_HOSTPATTERNFILL_H
+#define OFFLOAD_PLUGINS_NEXTGEN_HOST_SRC_HOSTPATTERNFILL_H
+
+#include <cstdint>
+#include <cstring>
+
+namespace llvm {
+namespace omp {
+namespace target {
+namespace plugin {
+
+/// Repeat \p PatternPtr across \p Size bytes starting at \p TgtPtr.
+///
+/// Each iteration copies at most \p PatternSize bytes, so a fill size that is
+/// not a multiple of the pattern size does not write past the destination.
+/// A non-positive pattern size or fill size is a no-op.
+inline void fillHostMemory(void *TgtPtr, const void *PatternPtr,
+                           int64_t PatternSize, int64_t Size) {
+  if (PatternSize <= 0 || Size <= 0)
+    return;
+
+  auto *Dst = static_cast<char *>(TgtPtr);
+  if (PatternSize == 1) {
+    std::memset(Dst, *static_cast<const char *>(PatternPtr),
+                static_cast<size_t>(Size));
+    return;
+  }
+
+  for (int64_t Offset = 0; Offset < Size;) {
+    int64_t ChunkSize = PatternSize;
+    if (ChunkSize > Size - Offset)
+      ChunkSize = Size - Offset;
+    std::memcpy(Dst + Offset, PatternPtr, static_cast<size_t>(ChunkSize));
+    Offset += ChunkSize;
+  }
+}
+
+} // namespace plugin
+} // namespace target
+} // namespace omp
+} // namespace llvm
+
+#endif // OFFLOAD_PLUGINS_NEXTGEN_HOST_SRC_HOSTPATTERNFILL_H
diff --git a/offload/plugins-nextgen/host/src/rtl.cpp b/offload/plugins-nextgen/host/src/rtl.cpp
index 55ada2f82c3600..a7bfcd9a34d469 100644
--- a/offload/plugins-nextgen/host/src/rtl.cpp
+++ b/offload/plugins-nextgen/host/src/rtl.cpp
@@ -20,6 +20,7 @@
 #include "Utils/ELF.h"
 
 #include "GlobalHandler.h"
+#include "HostPatternFill.h"
 #include "OffloadAPI.h"
 #include "OpenMP/OMPT/Callback.h"
 #include "PluginInterface.h"
@@ -303,15 +304,7 @@ struct GenELF64DeviceTy : public GenericDeviceTy {
   Error dataFillImpl(void *TgtPtr, const void *PatternPtr, int64_t PatternSize,
                      int64_t Size,
                      AsyncInfoWrapperTy &AsyncInfoWrapper) override {
-    if (PatternSize == 1) {
-      std::memset(TgtPtr, *static_cast<const char *>(PatternPtr), Size);
-    } else {
-      for (unsigned int Step = 0; Step < Size; Step += PatternSize) {
-        auto *Dst = static_cast<char *>(TgtPtr) + Step;
-        std::memcpy(Dst, PatternPtr, PatternSize);
-      }
-    }
-
+    fillHostMemory(TgtPtr, PatternPtr, PatternSize, Size);
     return Plugin::success();
   }
 
diff --git a/offload/unittests/OffloadAPI/CMakeLists.txt b/offload/unittests/OffloadAPI/CMakeLists.txt
index 292ea1eb4852f0..c2179b7522a248 100644
--- a/offload/unittests/OffloadAPI/CMakeLists.txt
+++ b/offload/unittests/OffloadAPI/CMakeLists.txt
@@ -32,6 +32,7 @@ add_offload_unittest("kernel"
     kernel/olLaunchKernelCooperative.cpp)
 
 add_offload_unittest("memory"
+    memory/HostPatternFillTest.cpp
     memory/olMemAlloc.cpp
     memory/olMemAllocAligned.cpp
     memory/olMemFill.cpp
@@ -41,6 +42,8 @@ add_offload_unittest("memory"
     memory/olGetMemInfo.cpp
     memory/olGetMemInfoSize.cpp
     memory/olMemRegister.cpp)
+target_include_directories("memory.unittests" PRIVATE
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../plugins-nextgen/host/src)
 
 add_offload_unittest("platform"
     platform/olGetPlatformInfo.cpp
diff --git a/offload/unittests/OffloadAPI/memory/HostPatternFillTest.cpp b/offload/unittests/OffloadAPI/memory/HostPatternFillTest.cpp
new file mode 100644
index 00000000000000..6c9ffafcdde0d2
--- /dev/null
+++ b/offload/unittests/OffloadAPI/memory/HostPatternFillTest.cpp
@@ -0,0 +1,72 @@
+//===------- Offload tests - host plugin pattern fill ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "HostPatternFill.h"
+#include <array>
+#include <cstdint>
+#include <gtest/gtest.h>
+
+using llvm::omp::target::plugin::fillHostMemory;
+
+TEST(HostPatternFill, RemainderDoesNotWritePastSize) {
+  constexpr int64_t FillSize = 5;
+  constexpr int64_t PatternSize = 4;
+  constexpr char Canary = static_cast<char>(0xAA);
+  std::array<char, 16> Buffer;
+  Buffer.fill(Canary);
+  const char Pattern[4] = {1, 2, 3, 4};
+
+  fillHostMemory(Buffer.data(), Pattern, PatternSize, FillSize);
+
+  EXPECT_EQ(Buffer[0], 1);
+  EXPECT_EQ(Buffer[1], 2);
+  EXPECT_EQ(Buffer[2], 3);
+  EXPECT_EQ(Buffer[3], 4);
+  EXPECT_EQ(Buffer[4], 1);
+  for (size_t I = static_cast<size_t>(FillSize); I < Buffer.size(); ++I)
+    EXPECT_EQ(Buffer[I], Canary) << "canary overwritten at byte " << I;
+}
+
+TEST(HostPatternFill, ZeroPatternSizeIsNoOp) {
+  std::array<char, 4> Buffer = {9, 9, 9, 9};
+  const char Pattern[4] = {1, 2, 3, 4};
+
+  fillHostMemory(Buffer.data(), Pattern, 0, Buffer.size());
+
+  EXPECT_EQ(Buffer, (std::array<char, 4>{9, 9, 9, 9}));
+}
+
+TEST(HostPatternFill, ZeroFillSizeIsNoOp) {
+  std::array<char, 4> Buffer = {9, 9, 9, 9};
+  const char Pattern[4] = {1, 2, 3, 4};
+
+  fillHostMemory(Buffer.data(), Pattern, 4, 0);
+
+  EXPECT_EQ(Buffer, (std::array<char, 4>{9, 9, 9, 9}));
+}
+
+TEST(HostPatternFill, RepeatsPatternWhenSizeIsMultiple) {
+  constexpr int64_t FillSize = 8;
+  constexpr int64_t PatternSize = 4;
+  std::array<char, 8> Buffer{};
+  const char Pattern[4] = {1, 2, 3, 4};
+
+  fillHostMemory(Buffer.data(), Pattern, PatternSize, FillSize);
+
+  EXPECT_EQ(Buffer, (std::array<char, 8>{1, 2, 3, 4, 1, 2, 3, 4}));
+}
+
+TEST(HostPatternFill, SingleByteFillsEntireDestination) {
+  std::array<char, 7> Buffer{};
+  const char Pattern = 0x5A;
+
+  fillHostMemory(Buffer.data(), &Pattern, 1, Buffer.size());
+
+  for (char Byte : Buffer)
+    EXPECT_EQ(Byte, Pattern);
+}



More information about the llvm-commits mailing list