Skip to content

Commit 22450a2

Browse files
committed
Avoid recursively throwing an exception from a custom operator new.
Allocating an exception object may recursively call our custom operator new, which would result in an infinite recursion and a crash. So only initiate the throw when the operator is called non-recursively. Otherwise, let the memory allocation proceed using malloc. This should hopefully fix thread constructor test crashes on OpenBSD.
1 parent 6abbda2 commit 22450a2

3 files changed

Lines changed: 87 additions & 9 deletions

File tree

test/threads/thread/constr/FArgs_pass.cpp

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@
2626
#include <boost/config.hpp>
2727

2828
unsigned throw_one = 0xFFFF;
29+
unsigned operator_new_recursion_level = 0u;
30+
31+
struct operator_new_recursion_counter
32+
{
33+
operator_new_recursion_counter() BOOST_NOEXCEPT_OR_NOTHROW
34+
{
35+
++operator_new_recursion_level;
36+
}
37+
~operator_new_recursion_counter() BOOST_NOEXCEPT_OR_NOTHROW
38+
{
39+
--operator_new_recursion_level;
40+
}
41+
};
2942

3043
#if defined _GLIBCXX_THROW
3144
void* operator new(std::size_t s) _GLIBCXX_THROW (std::bad_alloc)
@@ -34,10 +47,23 @@ void* operator new(std::size_t s)
3447
#endif
3548
{
3649
//std::cout << __FILE__ << ":" << __LINE__ << std::endl;
37-
if (throw_one == 0) throw std::bad_alloc();
38-
--throw_one;
50+
// Throwing an exception may recursively call operator new. If we're throwing std::bad_alloc,
51+
// this may cause infinite recursion and a crash. So only throw if we're at the top recursion level.
52+
operator_new_recursion_counter auto_counter;
53+
if (operator_new_recursion_level == 1u)
54+
{
55+
if (throw_one == 0) throw std::bad_alloc();
56+
--throw_one;
57+
}
3958
void* p = std::malloc(s);
40-
if (!p) throw std::bad_alloc();
59+
if (operator_new_recursion_level == 1u)
60+
{
61+
if (!p) throw std::bad_alloc();
62+
}
63+
else
64+
{
65+
std::abort();
66+
}
4167
return p;
4268
}
4369

test/threads/thread/constr/F_pass.cpp

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@
2626
#include <boost/config.hpp>
2727

2828
unsigned throw_one = 0xFFFF;
29+
unsigned operator_new_recursion_level = 0u;
30+
31+
struct operator_new_recursion_counter
32+
{
33+
operator_new_recursion_counter() BOOST_NOEXCEPT_OR_NOTHROW
34+
{
35+
++operator_new_recursion_level;
36+
}
37+
~operator_new_recursion_counter() BOOST_NOEXCEPT_OR_NOTHROW
38+
{
39+
--operator_new_recursion_level;
40+
}
41+
};
2942

3043
#if defined _GLIBCXX_THROW
3144
void* operator new(std::size_t s) _GLIBCXX_THROW (std::bad_alloc)
@@ -34,10 +47,23 @@ void* operator new(std::size_t s)
3447
#endif
3548
{
3649
//std::cout << __FILE__ << ":" << __LINE__ << std::endl;
37-
if (throw_one == 0) throw std::bad_alloc();
38-
--throw_one;
50+
// Throwing an exception may recursively call operator new. If we're throwing std::bad_alloc,
51+
// this may cause infinite recursion and a crash. So only throw if we're at the top recursion level.
52+
operator_new_recursion_counter auto_counter;
53+
if (operator_new_recursion_level == 1u)
54+
{
55+
if (throw_one == 0) throw std::bad_alloc();
56+
--throw_one;
57+
}
3958
void* p = std::malloc(s);
40-
if (!p) throw std::bad_alloc();
59+
if (operator_new_recursion_level == 1u)
60+
{
61+
if (!p) throw std::bad_alloc();
62+
}
63+
else
64+
{
65+
std::abort();
66+
}
4167
return p;
4268
}
4369

test/threads/thread/constr/lambda_pass.cpp

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,43 @@
2828
#if ! defined BOOST_NO_CXX11_LAMBDAS
2929

3030
unsigned throw_one = 0xFFFF;
31+
unsigned operator_new_recursion_level = 0u;
32+
33+
struct operator_new_recursion_counter
34+
{
35+
operator_new_recursion_counter() BOOST_NOEXCEPT_OR_NOTHROW
36+
{
37+
++operator_new_recursion_level;
38+
}
39+
~operator_new_recursion_counter() BOOST_NOEXCEPT_OR_NOTHROW
40+
{
41+
--operator_new_recursion_level;
42+
}
43+
};
3144

3245
#if defined _GLIBCXX_THROW
3346
void* operator new(std::size_t s) _GLIBCXX_THROW (std::bad_alloc)
3447
#else
3548
void* operator new(std::size_t s)
3649
#endif
3750
{
38-
if (throw_one == 0) throw std::bad_alloc();
39-
--throw_one;
51+
// Throwing an exception may recursively call operator new. If we're throwing std::bad_alloc,
52+
// this may cause infinite recursion and a crash. So only throw if we're at the top recursion level.
53+
operator_new_recursion_counter auto_counter;
54+
if (operator_new_recursion_level == 1u)
55+
{
56+
if (throw_one == 0) throw std::bad_alloc();
57+
--throw_one;
58+
}
4059
void* p = std::malloc(s);
41-
if (!p) throw std::bad_alloc();
60+
if (operator_new_recursion_level == 1u)
61+
{
62+
if (!p) throw std::bad_alloc();
63+
}
64+
else
65+
{
66+
std::abort();
67+
}
4268
return p;
4369
}
4470

0 commit comments

Comments
 (0)