[libc-commits] [libc] f340030 - [libc] Add custom operator new to handle allocation failures gracefully.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Sat Dec 10 16:29:20 PST 2022


Author: Siva Chandra Reddy
Date: 2022-12-11T00:29:04Z
New Revision: f3400305771d4e0084d2ebc8e3e5d2736ba3bd7c

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

LOG: [libc] Add custom operator new to handle allocation failures gracefully.

This patch adds the implementation of the custom operator new functions.
The implementation of the internal strdup has been updated to use
operator new for allocation.

We will make it a policy and document that all allocations have to go
through the libc's own operator new. A future change will also add
operator delete replacements and make it a policy that deallocations in
libc internal code have to go through those replacements.

Reviewed By: lntue

Differential Revision: https://reviews.llvm.org/D139584

Added: 
    libc/src/__support/CPP/new.h

Modified: 
    libc/src/string/CMakeLists.txt
    libc/src/string/allocating_string_utils.h
    libc/src/string/strdup.cpp
    libc/src/unistd/linux/getcwd.cpp
    libc/test/src/string/CMakeLists.txt
    libc/test/src/string/strdup_test.cpp

Removed: 
    


################################################################################
diff  --git a/libc/src/__support/CPP/new.h b/libc/src/__support/CPP/new.h
new file mode 100644
index 0000000000000..3becb9f26671f
--- /dev/null
+++ b/libc/src/__support/CPP/new.h
@@ -0,0 +1,72 @@
+//===-- Libc specific custom operator new and delete ------------*- 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_SUPPORT_CPP_NEW_H
+#define LLVM_LIBC_SRC_SUPPORT_CPP_NEW_H
+
+#include <stddef.h> // For size_t
+#include <stdlib.h> // For malloc, free etc.
+
+// Defining members in the std namespace is not preferred. But, we do it here
+// so that we can use it to define the operator new which takes std::align_val_t
+// argument.
+namespace std {
+
+enum class align_val_t : size_t {};
+
+} // namespace std
+
+namespace __llvm_libc {
+
+class AllocChecker {
+  bool success = false;
+  AllocChecker &operator=(bool status) {
+    success = status;
+    return *this;
+  }
+
+public:
+  AllocChecker() = default;
+  operator bool() const { return success; }
+
+  static void *alloc(size_t s, AllocChecker &ac) {
+    void *mem = ::malloc(s);
+    ac = (mem != nullptr);
+    return mem;
+  }
+
+  static void *aligned_alloc(size_t s, std::align_val_t align,
+                             AllocChecker &ac) {
+    void *mem = ::aligned_alloc(static_cast<size_t>(align), s);
+    ac = (mem != nullptr);
+    return mem;
+  }
+};
+
+} // namespace __llvm_libc
+
+inline void *operator new(size_t size, __llvm_libc::AllocChecker &ac) noexcept {
+  return __llvm_libc::AllocChecker::alloc(size, ac);
+}
+
+inline void *operator new(size_t size, std::align_val_t align,
+                          __llvm_libc::AllocChecker &ac) noexcept {
+  return __llvm_libc::AllocChecker::aligned_alloc(size, align, ac);
+}
+
+inline void *operator new[](size_t size,
+                            __llvm_libc::AllocChecker &ac) noexcept {
+  return __llvm_libc::AllocChecker::alloc(size, ac);
+}
+
+inline void *operator new[](size_t size, std::align_val_t align,
+                            __llvm_libc::AllocChecker &ac) noexcept {
+  return __llvm_libc::AllocChecker::aligned_alloc(size, align, ac);
+}
+
+#endif // LLVM_LIBC_SRC_SUPPORT_CPP_NEW_H

diff  --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt
index 422d24c131312..caeddc8995e68 100644
--- a/libc/src/string/CMakeLists.txt
+++ b/libc/src/string/CMakeLists.txt
@@ -16,8 +16,9 @@ add_header_library(
   HDRS
     allocating_string_utils.h
   DEPENDS
-    libc.include.stdlib
     .memory_utils.memcpy_implementation
+    libc.include.stdlib
+    libc.src.__support.CPP.optional
 )
 
 add_entrypoint_object(
@@ -150,7 +151,9 @@ add_entrypoint_object(
   DEPENDS
     .memory_utils.memcpy_implementation
     .string_utils
+    libc.include.errno
     libc.include.stdlib
+    libc.src.errno.errno
 )
 
 add_entrypoint_object(

diff  --git a/libc/src/string/allocating_string_utils.h b/libc/src/string/allocating_string_utils.h
index 32204007d622c..93d1c7ac8e351 100644
--- a/libc/src/string/allocating_string_utils.h
+++ b/libc/src/string/allocating_string_utils.h
@@ -9,24 +9,24 @@
 #ifndef LIBC_SRC_STRING_ALLOCATING_STRING_UTILS_H
 #define LIBC_SRC_STRING_ALLOCATING_STRING_UTILS_H
 
-#include "src/__support/CPP/bitset.h"
-#include "src/__support/common.h"
-#include "src/string/memory_utils/bzero_implementations.h"
-#include "src/string/memory_utils/memcpy_implementations.h"
+#include "src/__support/CPP/new.h"
+#include "src/__support/CPP/optional.h"
+#include "src/string/memory_utils/memcpy_implementations.h" // For string_length
 #include "src/string/string_utils.h"
+
 #include <stddef.h> // For size_t
-#include <stdlib.h> // For malloc
 
 namespace __llvm_libc {
 namespace internal {
 
-inline char *strdup(const char *src) {
+cpp::optional<char *> strdup(const char *src) {
   if (src == nullptr)
-    return nullptr;
+    return cpp::nullopt;
   size_t len = string_length(src) + 1;
-  char *newstr = reinterpret_cast<char *>(::malloc(len));
-  if (newstr == nullptr)
-    return nullptr;
+  AllocChecker ac;
+  char *newstr = new (ac) char[len];
+  if (!ac)
+    return cpp::nullopt;
   inline_memcpy(newstr, src, len);
   return newstr;
 }

diff  --git a/libc/src/string/strdup.cpp b/libc/src/string/strdup.cpp
index 9a52b298501b1..9aa0d5030c9bb 100644
--- a/libc/src/string/strdup.cpp
+++ b/libc/src/string/strdup.cpp
@@ -12,12 +12,18 @@
 
 #include "src/__support/common.h"
 
+#include <errno.h>
 #include <stdlib.h>
 
 namespace __llvm_libc {
 
 LLVM_LIBC_FUNCTION(char *, strdup, (const char *src)) {
-  return internal::strdup(src);
+  auto dup = internal::strdup(src);
+  if (dup)
+    return *dup;
+  if (src != nullptr)
+    errno = ENOMEM;
+  return nullptr;
 }
 
 } // namespace __llvm_libc

diff  --git a/libc/src/unistd/linux/getcwd.cpp b/libc/src/unistd/linux/getcwd.cpp
index 84a16b3d4efd1..67dea371ccb09 100644
--- a/libc/src/unistd/linux/getcwd.cpp
+++ b/libc/src/unistd/linux/getcwd.cpp
@@ -44,12 +44,12 @@ LLVM_LIBC_FUNCTION(char *, getcwd, (char *buf, size_t size)) {
     char pathbuf[PATH_MAX];
     if (!getcwd_syscall(pathbuf, PATH_MAX))
       return nullptr;
-    char *cwd = internal::strdup(pathbuf);
-    if (cwd == nullptr) {
+    auto cwd = internal::strdup(pathbuf);
+    if (!cwd) {
       errno = ENOMEM;
       return nullptr;
     }
-    return cwd;
+    return *cwd;
   } else if (size == 0) {
     errno = EINVAL;
     return nullptr;

diff  --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt
index 8b427cc2d116e..b5de08b2fbb03 100644
--- a/libc/test/src/string/CMakeLists.txt
+++ b/libc/test/src/string/CMakeLists.txt
@@ -141,8 +141,10 @@ add_libc_unittest(
   SRCS
     strdup_test.cpp
   DEPENDS
+    libc.include.errno
     libc.include.stdlib
     libc.src.string.strdup
+    libc.src.errno.errno
 )
 
 add_libc_unittest(

diff  --git a/libc/test/src/string/strdup_test.cpp b/libc/test/src/string/strdup_test.cpp
index 7820c1e7c5124..65760033156d6 100644
--- a/libc/test/src/string/strdup_test.cpp
+++ b/libc/test/src/string/strdup_test.cpp
@@ -8,12 +8,17 @@
 
 #include "src/string/strdup.h"
 #include "utils/UnitTest/Test.h"
+
+#include <errno.h>
 #include <stdlib.h>
 
 TEST(LlvmLibcStrDupTest, EmptyString) {
   const char *empty = "";
 
+  errno = 0;
   char *result = __llvm_libc::strdup(empty);
+  ASSERT_EQ(errno, 0);
+
   ASSERT_NE(result, static_cast<char *>(nullptr));
   ASSERT_NE(empty, const_cast<const char *>(result));
   ASSERT_STREQ(empty, result);
@@ -23,7 +28,9 @@ TEST(LlvmLibcStrDupTest, EmptyString) {
 TEST(LlvmLibcStrDupTest, AnyString) {
   const char *abc = "abc";
 
+  errno = 0;
   char *result = __llvm_libc::strdup(abc);
+  ASSERT_EQ(errno, 0);
 
   ASSERT_NE(result, static_cast<char *>(nullptr));
   ASSERT_NE(abc, const_cast<const char *>(result));
@@ -32,8 +39,9 @@ TEST(LlvmLibcStrDupTest, AnyString) {
 }
 
 TEST(LlvmLibcStrDupTest, NullPtr) {
-
+  errno = 0;
   char *result = __llvm_libc::strdup(nullptr);
+  ASSERT_EQ(errno, 0);
 
   ASSERT_EQ(result, static_cast<char *>(nullptr));
 }


        


More information about the libc-commits mailing list