Skip to content

Commit 9ee3e2c

Browse files
committed
Move std::move into secure_move, assert string ptr
1 parent f3562f8 commit 9ee3e2c

1 file changed

Lines changed: 38 additions & 14 deletions

File tree

cpp/src/arrow/util/secure_string.cc

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,49 @@
3030
#endif
3131

3232
#include "arrow/util/secure_string.h"
33+
#include "arrow/util/logging.h"
3334
#include "arrow/util/span.h"
3435

3536
namespace arrow::util {
3637

37-
SecureString::SecureString(SecureString&& other) noexcept
38-
: secret_(std::move(other.secret_)) {
38+
/// Note:
39+
/// A string std::string is securely moved into a SecureString in two steps:
40+
/// 1. the std::string is moved via std::move(string)
41+
/// 2. the std::string is securely cleared
42+
///
43+
/// The std::move has two different effects, depending on the size of the string.
44+
/// A very short string (called local string) stores the string in a local buffer,
45+
/// a long string stores a pointer to allocated memory that stores the string.
46+
///
47+
/// If the string is a small string, std::move copies the local buffer.
48+
/// If the string is a long string, std::move moves the pointer and then resets the
49+
/// string size to 0 (which turns the string into a local string).
50+
///
51+
/// In both cases, after a std::move(string), the string uses the local buffer.
52+
///
53+
/// Thus, after a std::move(string), calling SecureClear(std::string*) only
54+
/// securely clears the **local buffer** of the string. Therefore, std::move(string)
55+
/// must move the pointer of long string into SecureString (which later clears the string).
56+
/// Otherwise, the content of the string cannot be securely cleared.
57+
///
58+
/// This condition is checked by secure_move.
59+
60+
void secure_move(std::string& string, std::string& dst) {
61+
auto ptr = string.data();
62+
dst = std::move(string);
63+
64+
// We require the buffer address string.data() to remain (not be freed),
65+
// or reused by dst. Otherwise, we cannot securely clear string after this move
66+
ARROW_CHECK(string.data() == ptr || dst.data() == ptr);
67+
}
68+
69+
SecureString::SecureString(SecureString&& other) noexcept {
70+
secure_move(other.secret_, secret_);
3971
other.Dispose();
4072
}
4173

42-
SecureString::SecureString(std::string&& secret) noexcept : secret_(std::move(secret)) {
74+
SecureString::SecureString(std::string&& secret) noexcept {
75+
secure_move(secret, secret_);
4376
SecureClear(&secret);
4477
}
4578

@@ -51,7 +84,7 @@ SecureString& SecureString::operator=(SecureString&& other) noexcept {
5184
return *this;
5285
}
5386
Dispose();
54-
secret_ = std::move(other.secret_);
87+
secure_move(other.secret_, secret_);
5588
other.Dispose();
5689
return *this;
5790
}
@@ -68,16 +101,7 @@ SecureString& SecureString::operator=(const SecureString& other) {
68101

69102
SecureString& SecureString::operator=(std::string&& secret) noexcept {
70103
Dispose();
71-
// std::string implementation may distinguish between local string (a very short string)
72-
// and non-local (longer) strings. The former stores the string in a local buffer, the
73-
// latter stores a pointer to allocated memory that stores the string.
74-
//
75-
// If secret is a local string, copies local buffer, resets size to 0
76-
// - requires secure cleaning the local buffer
77-
// If secret is longer, moves the pointer to secret_, resets to 0, uses local buffer
78-
// - does not require cleaning anything
79-
secret_ = std::move(secret);
80-
// cleans only the local buffer of secret as this always is a local string by now
104+
secure_move(secret, secret_);
81105
SecureClear(&secret);
82106
return *this;
83107
}

0 commit comments

Comments
 (0)