[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