[libc-commits] [libc] f6f42af - [libc] Add `shm_open/shm_unlink` (#84974)

via libc-commits libc-commits at lists.llvm.org
Mon Mar 18 08:40:12 PDT 2024


Author: Schrodinger ZHU Yifan
Date: 2024-03-18T11:40:07-04:00
New Revision: f6f42af06f6fe6a78f044686a36e4995d4f42ac5

URL: https://github.com/llvm/llvm-project/commit/f6f42af06f6fe6a78f044686a36e4995d4f42ac5
DIFF: https://github.com/llvm/llvm-project/commit/f6f42af06f6fe6a78f044686a36e4995d4f42ac5.diff

LOG: [libc] Add `shm_open/shm_unlink` (#84974)

Added: 
    libc/src/sys/mman/linux/shm_common.h
    libc/src/sys/mman/linux/shm_open.cpp
    libc/src/sys/mman/linux/shm_unlink.cpp
    libc/src/sys/mman/shm_open.h
    libc/src/sys/mman/shm_unlink.h
    libc/test/src/sys/mman/linux/shm_test.cpp

Modified: 
    libc/config/linux/aarch64/entrypoints.txt
    libc/config/linux/api.td
    libc/config/linux/riscv/entrypoints.txt
    libc/config/linux/x86_64/entrypoints.txt
    libc/docs/dev/undefined_behavior.rst
    libc/spec/posix.td
    libc/src/__support/CPP/string_view.h
    libc/src/sys/mman/CMakeLists.txt
    libc/src/sys/mman/linux/CMakeLists.txt
    libc/test/src/__support/CPP/stringview_test.cpp
    libc/test/src/sys/mman/linux/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 43c9e81f17833e..a48a94f715eb7e 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -216,6 +216,8 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.sys.mman.mlockall
     libc.src.sys.mman.munlockall
     libc.src.sys.mman.msync
+    libc.src.sys.mman.shm_open
+    libc.src.sys.mman.shm_unlink
 
     # sys/random.h entrypoints
     libc.src.sys.random.getrandom

diff  --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index 04d720daf9509c..e9e82c5d478945 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -117,7 +117,7 @@ def SchedAPI : PublicAPI<"sched.h"> {
 }
 
 def SysMManAPI : PublicAPI<"sys/mman.h"> {
-  let Types = ["off_t", "size_t"];
+  let Types = ["off_t", "size_t", "mode_t"];
 }
 
 def SignalAPI : PublicAPI<"signal.h"> {

diff  --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 99ef84d3f73974..5e28378b403a79 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -221,6 +221,8 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.sys.mman.mlockall
     libc.src.sys.mman.munlockall
     libc.src.sys.mman.msync
+    libc.src.sys.mman.shm_open
+    libc.src.sys.mman.shm_unlink
 
     # sys/random.h entrypoints
     libc.src.sys.random.getrandom

diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 99182e7f92ac09..ee02488ca7cd7c 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -229,6 +229,8 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.sys.mman.mlockall
     libc.src.sys.mman.munlockall
     libc.src.sys.mman.msync
+    libc.src.sys.mman.shm_open
+    libc.src.sys.mman.shm_unlink
 
     # sys/random.h entrypoints
     libc.src.sys.random.getrandom

diff  --git a/libc/docs/dev/undefined_behavior.rst b/libc/docs/dev/undefined_behavior.rst
index 6e73a305e8e054..3a06b7007febd4 100644
--- a/libc/docs/dev/undefined_behavior.rst
+++ b/libc/docs/dev/undefined_behavior.rst
@@ -70,3 +70,7 @@ Design Decisions
 Resizable Tables for hsearch
 ----------------------------
 The POSIX.1 standard does not delineate the behavior consequent to invoking hsearch or hdestroy without prior initialization of the hash table via hcreate. Furthermore, the standard does not specify the outcomes of successive invocations of hsearch absent intervening hdestroy calls. Libraries such as MUSL and Glibc do not apply checks to these scenarios, potentially leading to memory corruption or leakage. Conversely, FreeBSD's libc and Bionic automatically initialize the hash table to a minimal size if it is found uninitialized, and proceeding to destroy the table only if initialization has occurred. This approach also avoids redundant table allocation if an initialized hash table is already present. Given that the hash table starts with a minimal size, resizing becomes necessary to accommodate additional user insertions. LLVM's libc mirrors the approach of FreeBSD's libc and Bionic, owing to its enhanced robustness and user-friendliness. Notably, such resizing behavior itself aligns with POSIX.1 standards, which explicitly permit implementations to modify the capacity of the hash table.
+
+Path without Leading Slashs in shm_open
+----------------------------------------
+POSIX.1 leaves that when the name of a shared memory object does not begin with a slash, the behavior is implementation defined. In such cases, the shm_open in LLVM libc is implemented to behave as if the name began with a slash.

diff  --git a/libc/spec/posix.td b/libc/spec/posix.td
index 591919aac95dd5..26f41b61dc0015 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -246,6 +246,7 @@ def POSIX : StandardSpec<"POSIX"> {
       [
         SizeTType,
         OffTType,
+        ModeTType,
       ],
       [], // Enumerations
       [
@@ -310,6 +311,16 @@ def POSIX : StandardSpec<"POSIX"> {
           RetValSpec<IntType>,
           [ArgSpec<VoidPtr>, ArgSpec<SizeTType>, ArgSpec<IntType>]
         >,
+        FunctionSpec<
+          "shm_open",
+          RetValSpec<IntType>,
+          [ArgSpec<ConstCharPtr>, ArgSpec<IntType>, ArgSpec<ModeTType>]
+        >,
+        FunctionSpec<
+          "shm_unlink",
+          RetValSpec<IntType>,
+          [ArgSpec<ConstCharPtr>]
+        >,
       ]
   >;
 

diff  --git a/libc/src/__support/CPP/string_view.h b/libc/src/__support/CPP/string_view.h
index d23aa261ca0614..8aa96fa87f6986 100644
--- a/libc/src/__support/CPP/string_view.h
+++ b/libc/src/__support/CPP/string_view.h
@@ -179,7 +179,8 @@ class string_view {
   LIBC_INLINE char back() const { return Data[Len - 1]; }
 
   // Finds the first occurence of c in this view, starting at position From.
-  LIBC_INLINE size_t find_first_of(const char c, size_t From = 0) const {
+  LIBC_INLINE constexpr size_t find_first_of(const char c,
+                                             size_t From = 0) const {
     for (size_t Pos = From; Pos < size(); ++Pos)
       if ((*this)[Pos] == c)
         return Pos;
@@ -187,13 +188,29 @@ class string_view {
   }
 
   // Finds the last occurence of c in this view, ending at position End.
-  LIBC_INLINE size_t find_last_of(const char c, size_t End = npos) const {
+  LIBC_INLINE constexpr size_t find_last_of(const char c,
+                                            size_t End = npos) const {
     End = End >= size() ? size() : End + 1;
     for (; End > 0; --End)
       if ((*this)[End - 1] == c)
         return End - 1;
     return npos;
   }
+
+  // Finds the first character not equal to c in this view, starting at position
+  // From.
+  LIBC_INLINE constexpr size_t find_first_not_of(const char c,
+                                                 size_t From = 0) const {
+    for (size_t Pos = From; Pos < size(); ++Pos)
+      if ((*this)[Pos] != c)
+        return Pos;
+    return npos;
+  }
+
+  // Check if this view contains the given character.
+  LIBC_INLINE constexpr bool contains(char c) const {
+    return find_first_of(c) != npos;
+  }
 };
 
 } // namespace cpp

diff  --git a/libc/src/sys/mman/CMakeLists.txt b/libc/src/sys/mman/CMakeLists.txt
index b49f73873c2006..9c74202a09f035 100644
--- a/libc/src/sys/mman/CMakeLists.txt
+++ b/libc/src/sys/mman/CMakeLists.txt
@@ -85,3 +85,17 @@ add_entrypoint_object(
   DEPENDS
     .${LIBC_TARGET_OS}.msync
 )
+
+add_entrypoint_object(
+  shm_open
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.shm_open
+)
+
+add_entrypoint_object(
+  shm_unlink
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.shm_unlink
+)

diff  --git a/libc/src/sys/mman/linux/CMakeLists.txt b/libc/src/sys/mman/linux/CMakeLists.txt
index 04086ee5d33254..00f4f0e64ec06b 100644
--- a/libc/src/sys/mman/linux/CMakeLists.txt
+++ b/libc/src/sys/mman/linux/CMakeLists.txt
@@ -152,3 +152,40 @@ add_entrypoint_object(
     libc.src.__support.OSUtil.osutil
     libc.src.errno.errno
 )
+
+add_header_library(
+  shm_common
+  HDRS
+    shm_common.h
+  DEPENDS
+    libc.src.__support.CPP.array
+    libc.src.__support.CPP.string_view
+    libc.src.__support.CPP.optional
+    libc.src.__support.common
+    libc.src.errno.errno
+    libc.src.string.memory_utils.inline_memcpy
+)
+
+add_entrypoint_object(
+  shm_open
+  SRCS
+    shm_open.cpp
+  HDRS
+    ../shm_open.h
+  DEPENDS
+    libc.src.fcntl.open
+    libc.include.llvm-libc-macros.fcntl_macros
+    libc.include.llvm-libc-types.mode_t
+    .shm_common
+)
+
+add_entrypoint_object(
+  shm_unlink
+  SRCS
+    shm_unlink.cpp
+  HDRS
+    ../shm_unlink.h
+  DEPENDS
+    libc.src.unistd.unlink
+    .shm_common
+)

diff  --git a/libc/src/sys/mman/linux/shm_common.h b/libc/src/sys/mman/linux/shm_common.h
new file mode 100644
index 00000000000000..6f2a3fdc71b647
--- /dev/null
+++ b/libc/src/sys/mman/linux/shm_common.h
@@ -0,0 +1,53 @@
+//===---------- Shared implementations for shm_open/shm_unlink ------------===//
+//
+// 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 "src/__support/CPP/array.h"
+#include "src/__support/CPP/optional.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/errno/libc_errno.h"
+#include "src/string/memory_utils/inline_memcpy.h"
+
+// TODO: Get PATH_MAX via https://github.com/llvm/llvm-project/issues/85121
+#include <linux/limits.h>
+
+namespace LIBC_NAMESPACE {
+
+namespace shm_common {
+
+LIBC_INLINE_VAR constexpr cpp::string_view SHM_PREFIX = "/dev/shm/";
+using SHMPath = cpp::array<char, NAME_MAX + SHM_PREFIX.size() + 1>;
+
+LIBC_INLINE cpp::optional<SHMPath> translate_name(cpp::string_view name) {
+  // trim leading slashes
+  size_t offset = name.find_first_not_of('/');
+  if (offset == cpp::string_view::npos) {
+    libc_errno = EINVAL;
+    return cpp::nullopt;
+  }
+  name = name.substr(offset);
+
+  // check the name
+  if (name.size() > NAME_MAX) {
+    libc_errno = ENAMETOOLONG;
+    return cpp::nullopt;
+  }
+  if (name == "." || name == ".." || name.contains('/')) {
+    libc_errno = EINVAL;
+    return cpp::nullopt;
+  }
+
+  // prepend the prefix
+  SHMPath buffer;
+  inline_memcpy(buffer.data(), SHM_PREFIX.data(), SHM_PREFIX.size());
+  inline_memcpy(buffer.data() + SHM_PREFIX.size(), name.data(), name.size());
+  buffer[SHM_PREFIX.size() + name.size()] = '\0';
+  return buffer;
+}
+} // namespace shm_common
+
+} // namespace LIBC_NAMESPACE

diff  --git a/libc/src/sys/mman/linux/shm_open.cpp b/libc/src/sys/mman/linux/shm_open.cpp
new file mode 100644
index 00000000000000..0d39b8b4e53dbf
--- /dev/null
+++ b/libc/src/sys/mman/linux/shm_open.cpp
@@ -0,0 +1,25 @@
+//===---------- Linux implementation of the shm_open function -------------===//
+//
+// 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 "src/sys/mman/shm_open.h"
+#include "llvm-libc-macros/fcntl-macros.h"
+#include "src/fcntl/open.h"
+#include "src/sys/mman/linux/shm_common.h"
+
+namespace LIBC_NAMESPACE {
+
+static constexpr int DEFAULT_OFLAGS = O_NOFOLLOW | O_CLOEXEC | O_NONBLOCK;
+
+LLVM_LIBC_FUNCTION(int, shm_open, (const char *name, int oflags, mode_t mode)) {
+  using namespace shm_common;
+  if (cpp::optional<SHMPath> buffer = translate_name(name))
+    return open(buffer->data(), oflags | DEFAULT_OFLAGS, mode);
+  return -1;
+}
+
+} // namespace LIBC_NAMESPACE

diff  --git a/libc/src/sys/mman/linux/shm_unlink.cpp b/libc/src/sys/mman/linux/shm_unlink.cpp
new file mode 100644
index 00000000000000..32f48d3e3e7187
--- /dev/null
+++ b/libc/src/sys/mman/linux/shm_unlink.cpp
@@ -0,0 +1,22 @@
+//===---------- Linux implementation of the shm_unlink function -----------===//
+//
+// 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 "src/sys/mman/shm_unlink.h"
+#include "src/sys/mman/linux/shm_common.h"
+#include "src/unistd/unlink.h"
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(int, shm_unlink, (const char *name)) {
+  using namespace shm_common;
+  if (cpp::optional<SHMPath> buffer = translate_name(name))
+    return unlink(buffer->data());
+  return -1;
+}
+
+} // namespace LIBC_NAMESPACE

diff  --git a/libc/src/sys/mman/shm_open.h b/libc/src/sys/mman/shm_open.h
new file mode 100644
index 00000000000000..91796d7b5c0501
--- /dev/null
+++ b/libc/src/sys/mman/shm_open.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for shm_open function -------------*- 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 LLVM_LIBC_SRC_SYS_MMAN_SHM_OPEN_H
+#define LLVM_LIBC_SRC_SYS_MMAN_SHM_OPEN_H
+
+#include <llvm-libc-types/mode_t.h>
+
+namespace LIBC_NAMESPACE {
+
+int shm_open(const char *name, int oflag, mode_t mode);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_SYS_MMAN_SHM_OPEN_H

diff  --git a/libc/src/sys/mman/shm_unlink.h b/libc/src/sys/mman/shm_unlink.h
new file mode 100644
index 00000000000000..c38c06adbaa292
--- /dev/null
+++ b/libc/src/sys/mman/shm_unlink.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for shm_unlink function ------------*- 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 LLVM_LIBC_SRC_SYS_MMAN_SHM_UNLINK_H
+#define LLVM_LIBC_SRC_SYS_MMAN_SHM_UNLINK_H
+
+namespace LIBC_NAMESPACE {
+
+int shm_unlink(const char *name);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_SYS_MMAN_SHM_UNLINK_H

diff  --git a/libc/test/src/__support/CPP/stringview_test.cpp b/libc/test/src/__support/CPP/stringview_test.cpp
index 33eb8ab4765728..6b68f2a1c47a9d 100644
--- a/libc/test/src/__support/CPP/stringview_test.cpp
+++ b/libc/test/src/__support/CPP/stringview_test.cpp
@@ -174,3 +174,111 @@ TEST(LlvmLibcStringViewTest, FindLastOf) {
   ASSERT_EQ(Empty1.find_last_of('a', 0), string_view::npos);
   ASSERT_EQ(Empty1.find_last_of('a', 123), string_view::npos);
 }
+
+TEST(LlvmLibcStringViewTest, FindFirstNotOf) {
+  string_view Tmp("abada");
+
+  EXPECT_EQ(Tmp.find_first_not_of('a'), size_t(1));
+  EXPECT_EQ(Tmp.find_first_not_of('a', 123), string_view::npos);
+  EXPECT_EQ(Tmp.find_first_not_of('a', 5), string_view::npos);
+  EXPECT_EQ(Tmp.find_first_not_of('a', 4), string_view::npos);
+  EXPECT_EQ(Tmp.find_first_not_of('a', 3), size_t(3));
+  EXPECT_EQ(Tmp.find_first_not_of('a', 2), size_t(3));
+  EXPECT_EQ(Tmp.find_first_not_of('a', 1), size_t(1));
+  EXPECT_EQ(Tmp.find_first_not_of('a', 0), size_t(1));
+
+  EXPECT_EQ(Tmp.find_first_not_of('b'), size_t(0));
+  EXPECT_EQ(Tmp.find_first_not_of('b', 123), string_view::npos);
+  EXPECT_EQ(Tmp.find_first_not_of('b', 5), string_view::npos);
+  EXPECT_EQ(Tmp.find_first_not_of('b', 4), size_t(4));
+  EXPECT_EQ(Tmp.find_first_not_of('b', 3), size_t(3));
+  EXPECT_EQ(Tmp.find_first_not_of('b', 2), size_t(2));
+  EXPECT_EQ(Tmp.find_first_not_of('b', 1), size_t(2));
+  EXPECT_EQ(Tmp.find_first_not_of('b', 0), size_t(0));
+
+  EXPECT_EQ(Tmp.find_first_not_of('d'), size_t(0));
+  EXPECT_EQ(Tmp.find_first_not_of('d', 123), string_view::npos);
+  EXPECT_EQ(Tmp.find_first_not_of('d', 5), string_view::npos);
+  EXPECT_EQ(Tmp.find_first_not_of('d', 4), size_t(4));
+  EXPECT_EQ(Tmp.find_first_not_of('d', 3), size_t(4));
+  EXPECT_EQ(Tmp.find_first_not_of('d', 2), size_t(2));
+  EXPECT_EQ(Tmp.find_first_not_of('d', 1), size_t(1));
+  EXPECT_EQ(Tmp.find_first_not_of('d', 0), size_t(0));
+
+  EXPECT_EQ(Tmp.find_first_not_of('e'), size_t(0));
+  EXPECT_EQ(Tmp.find_first_not_of('e', 123), string_view::npos);
+  EXPECT_EQ(Tmp.find_first_not_of('e', 5), string_view::npos);
+  EXPECT_EQ(Tmp.find_first_not_of('e', 4), size_t(4));
+  EXPECT_EQ(Tmp.find_first_not_of('e', 3), size_t(3));
+  EXPECT_EQ(Tmp.find_first_not_of('e', 2), size_t(2));
+  EXPECT_EQ(Tmp.find_first_not_of('e', 1), size_t(1));
+  EXPECT_EQ(Tmp.find_first_not_of('e', 0), size_t(0));
+
+  string_view Empty;
+  EXPECT_EQ(Empty.find_first_not_of('a'), string_view::npos);
+  EXPECT_EQ(Empty.find_first_not_of('a', 0), string_view::npos);
+  EXPECT_EQ(Empty.find_first_not_of('a', 123), string_view::npos);
+
+  string_view Empty1("");
+  EXPECT_EQ(Empty1.find_first_not_of('a'), string_view::npos);
+  EXPECT_EQ(Empty1.find_first_not_of('a', 0), string_view::npos);
+  EXPECT_EQ(Empty1.find_first_not_of('a', 123), string_view::npos);
+
+  string_view Full("aaaaaaa");
+  EXPECT_EQ(Full.find_first_not_of('a'), string_view::npos);
+  EXPECT_EQ(Full.find_first_not_of('a', 0), string_view::npos);
+  EXPECT_EQ(Full.find_first_not_of('a', 123), string_view::npos);
+
+  EXPECT_EQ(Full.find_first_not_of('b'), size_t(0));
+  EXPECT_EQ(Full.find_first_not_of('b', 0), size_t(0));
+  EXPECT_EQ(Full.find_first_not_of('b', 123), string_view::npos);
+}
+
+TEST(LlvmLibcStringViewTest, Contains) {
+  string_view Empty;
+  for (char c = 'a'; c < 'z'; ++c)
+    EXPECT_FALSE(Empty.contains(c));
+
+  string_view Tmp("abada");
+  EXPECT_TRUE(Tmp.contains('a'));
+  EXPECT_TRUE(Tmp.contains('b'));
+  EXPECT_FALSE(Tmp.contains('c'));
+  EXPECT_TRUE(Tmp.contains('d'));
+  EXPECT_FALSE(Tmp.contains('e'));
+
+  EXPECT_TRUE(Tmp.substr(1).contains('a'));
+  EXPECT_TRUE(Tmp.substr(1).contains('b'));
+  EXPECT_FALSE(Tmp.substr(1).contains('c'));
+  EXPECT_TRUE(Tmp.substr(1).contains('d'));
+  EXPECT_FALSE(Tmp.substr(1).contains('e'));
+
+  EXPECT_TRUE(Tmp.substr(2).contains('a'));
+  EXPECT_FALSE(Tmp.substr(2).contains('b'));
+  EXPECT_FALSE(Tmp.substr(2).contains('c'));
+  EXPECT_TRUE(Tmp.substr(2).contains('d'));
+  EXPECT_FALSE(Tmp.substr(2).contains('e'));
+
+  EXPECT_TRUE(Tmp.substr(3).contains('a'));
+  EXPECT_FALSE(Tmp.substr(3).contains('b'));
+  EXPECT_FALSE(Tmp.substr(3).contains('c'));
+  EXPECT_TRUE(Tmp.substr(3).contains('d'));
+  EXPECT_FALSE(Tmp.substr(3).contains('e'));
+
+  EXPECT_TRUE(Tmp.substr(4).contains('a'));
+  EXPECT_FALSE(Tmp.substr(4).contains('b'));
+  EXPECT_FALSE(Tmp.substr(4).contains('c'));
+  EXPECT_FALSE(Tmp.substr(4).contains('d'));
+  EXPECT_FALSE(Tmp.substr(4).contains('e'));
+
+  EXPECT_FALSE(Tmp.substr(5).contains('a'));
+  EXPECT_FALSE(Tmp.substr(5).contains('b'));
+  EXPECT_FALSE(Tmp.substr(5).contains('c'));
+  EXPECT_FALSE(Tmp.substr(5).contains('d'));
+  EXPECT_FALSE(Tmp.substr(5).contains('e'));
+
+  EXPECT_FALSE(Tmp.substr(6).contains('a'));
+  EXPECT_FALSE(Tmp.substr(6).contains('b'));
+  EXPECT_FALSE(Tmp.substr(6).contains('c'));
+  EXPECT_FALSE(Tmp.substr(6).contains('d'));
+  EXPECT_FALSE(Tmp.substr(6).contains('e'));
+}

diff  --git a/libc/test/src/sys/mman/linux/CMakeLists.txt b/libc/test/src/sys/mman/linux/CMakeLists.txt
index 6f7fc34d8d3ce3..0762a2d846a8e0 100644
--- a/libc/test/src/sys/mman/linux/CMakeLists.txt
+++ b/libc/test/src/sys/mman/linux/CMakeLists.txt
@@ -127,3 +127,23 @@ add_libc_unittest(
     libc.src.unistd.sysconf
     libc.test.UnitTest.ErrnoSetterMatcher
 )
+
+add_libc_unittest(
+  shm_test
+  SUITE
+    libc_sys_mman_unittests
+  SRCS
+    shm_test.cpp
+  DEPENDS
+    libc.include.sys_mman
+    libc.include.sys_syscall
+    libc.src.errno.errno
+    libc.src.sys.mman.shm_open
+    libc.src.sys.mman.shm_unlink
+    libc.src.sys.mman.mmap
+    libc.src.sys.mman.munmap
+    libc.src.unistd.ftruncate
+    libc.src.unistd.close
+    libc.src.__support.OSUtil.osutil
+    libc.test.UnitTest.ErrnoSetterMatcher
+)

diff  --git a/libc/test/src/sys/mman/linux/shm_test.cpp b/libc/test/src/sys/mman/linux/shm_test.cpp
new file mode 100644
index 00000000000000..3b1a2aa33b56ad
--- /dev/null
+++ b/libc/test/src/sys/mman/linux/shm_test.cpp
@@ -0,0 +1,77 @@
+//===-- Unittests for shm_open/shm_unlink ---------------------------------===//
+//
+// 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 "src/__support/OSUtil/syscall.h"
+#include "src/sys/mman/mmap.h"
+#include "src/sys/mman/munmap.h"
+#include "src/sys/mman/shm_open.h"
+#include "src/sys/mman/shm_unlink.h"
+#include "src/unistd/close.h"
+#include "src/unistd/ftruncate.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/LibcTest.h"
+#include <asm-generic/fcntl.h>
+#include <sys/syscall.h>
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+// since shm_open/shm_unlink are wrappers around open/unlink, we only focus on
+// testing basic cases and name conversions.
+
+TEST(LlvmLibcShmTest, Basic) {
+  const char *name = "/test_shm_open";
+  int fd;
+  ASSERT_THAT(fd = LIBC_NAMESPACE::shm_open(name, O_CREAT | O_RDWR, 0666),
+              returns(GE(0)).with_errno(EQ(0)));
+
+  // check that FD_CLOEXEC is set by default.
+  // TODO: use fcntl when implemented.
+  // https://github.com/llvm/llvm-project/issues/84968
+  long flag = LIBC_NAMESPACE::syscall_impl(SYS_fcntl, fd, F_GETFD);
+  ASSERT_GE(static_cast<int>(flag), 0);
+  EXPECT_NE(static_cast<int>(flag) & FD_CLOEXEC, 0);
+
+  // allocate space using ftruncate
+  ASSERT_THAT(LIBC_NAMESPACE::ftruncate(fd, 4096), Succeeds());
+  // map the shared memory
+  void *addr = LIBC_NAMESPACE::mmap(nullptr, 4096, PROT_READ | PROT_WRITE,
+                                    MAP_SHARED, fd, 0);
+  ASSERT_NE(addr, MAP_FAILED);
+  // just write random data to the shared memory
+  char data[] = "Despite its name, LLVM has little to do with traditional "
+                "virtual machines.";
+  for (size_t i = 0; i < sizeof(data); ++i)
+    static_cast<char *>(addr)[i] = data[i];
+
+  // close fd does not affect the mapping
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds());
+  for (size_t i = 0; i < sizeof(data); ++i)
+    EXPECT_EQ(static_cast<char *>(addr)[i], data[i]);
+
+  // unmap the shared memory
+  ASSERT_THAT(LIBC_NAMESPACE::munmap(addr, 4096), Succeeds());
+  // remove the shared memory
+  ASSERT_THAT(LIBC_NAMESPACE::shm_unlink(name), Succeeds());
+}
+
+TEST(LlvmLibcShmTest, NameConversion) {
+  const char *name = "////test_shm_open";
+  int fd;
+  ASSERT_THAT(fd = LIBC_NAMESPACE::shm_open(name, O_CREAT | O_RDWR, 0666),
+              returns(GE(0)).with_errno(EQ(0)));
+  ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::shm_unlink(name), Succeeds());
+
+  ASSERT_THAT(LIBC_NAMESPACE::shm_open("/123/123", O_CREAT | O_RDWR, 0666),
+              Fails(EINVAL));
+
+  ASSERT_THAT(LIBC_NAMESPACE::shm_open("/.", O_CREAT | O_RDWR, 0666),
+              Fails(EINVAL));
+
+  ASSERT_THAT(LIBC_NAMESPACE::shm_open("/..", O_CREAT | O_RDWR, 0666),
+              Fails(EINVAL));
+}


        


More information about the libc-commits mailing list