Показать сообщение отдельно

  #2  
Старый 25.12.2019, 10:28
san0
Постоянный
Регистрация: 15.12.2013
Сообщений: 412
С нами: 6530249

Репутация: 133
По умолчанию

Цитата:
Сообщение от _=Gigant=_  

What is the best & safest way to clean std::string from memory ?
std::string example;
1. example.clear();
2. example.resize(0);
3. std::fill(example.begin(), example.end(), 0);
Hi there, such a cool question. Basically, have to distinguish what does "best & safest" mean in that context

By the rules of RTTI, standard definition of an object constructed in some arbitrary block (let's not include here some special cases like overloaded form of operator new()) will be destructed at the end of that block

C++:





Код:
{
std
::
string my_string
{
"String!"
}
;
// ...
// std::basic_string::~basic_string will be called
}


So you ain't gotta do that by yourself, no such need for neither .resize, nor free(), delete(), std::fill and others

In case 'safest method' stands for making sure data you operate within act must be zeroed / cleared, especially in cases when std::string will be used for storing sensitive data (passwords and others), well, there's no portable and unambiguous answer.

Doing manual .clear call does not give any guarantee that this call won't be optimized

Proof: https://godbolt.org/z/rdCQyQ

Same goes for the std::basic_string::~basic_string, it might or mightn't clear used memory

libc++, for example, does this

libcxx/include/string at master · llvm-mirror/libcxx

Project moved to: https://github.com/llvm/llvm-project - llvm-mirror/libcxx

github.com


For a kick-off, there's has to be some function to do zeroing above some memory block with confidentially that this call/expression won't be optimized away by the compiler.

You might take a look at the following:

1) memset_s from C11. Even though it must be portable, there are some problems with C11 in, for instance, Visual Studio. Moreover, __STDC_WANT_LIB_EXT1__ and __STDC_WANT_LIB_EXT1__ must be used in any platform and there's another possible issue - MacOS might not work with those macroeses.

Код:





Код:
#define __STDC_WANT_LIB_EXT1__ 1
#include 

// available to use function
// errno_t memset_s(void * restrict s, rsize_t smax, int c, rsize_t n)


2) write memset_s by yourself, don't miss volatile modifier

Код:





Код:
void zerofill(void *data, size_t size) {
    for (auto p = reinterpret_cast(data); size; size--) {
      *(p + size) = 0;
    }
}




3) User platform-provided solutions

bzero on macOS doesn't have any rule for compiler optimizations, so skip that

SecureZeroMemory on Windows does exactly what needed

SecureZeroMemory function (Windows)

docs.microsoft.com


Secondly, there's a need to know what is the way zeroing might be perfomed

1) direct data access

Before c++17, you could give a try to &str[0], but it has undefined behavior when pos == size()

Since c++17, std::basic_string::data will return non const pointer if target string was not const

2) might try std::allocator by calling std::string::get_allocator, but it's sort of deprecated

To implement this like it should be in modern c++, you can try usual class inheritance, but i'd like to recommend do this with std::unique_ptr, since it has support for the disposal function

Of course, this doesn't cover cases you're dealing with classical OOP model and can do cleaning at your class destructor

Here are the tests i've done

Цитата:
Сообщение от Спойлер  


C++:





Код:
#include 
#include 
#include 
#include 
#include 
#include 
#include 
char
*
get_string_data
(
std
::
string
&
str
)
{
if
constexpr
(
!
std
::
is_const

(
)
.
data
(
)
)
>
(
)
)
return
str
.
data
(
)
;
else
return
&
str
[
0
]
;
}
namespace
secure
{
static
void
safe_clear
(
void
*
data
,
size_t size
)
{
for
(
auto
p
=
reinterpret_cast

(
data
)
;
size
;
size
--
)
{
*
(
p
+
size
)
=
0
;
}
}
static
void
string_clear
(
std
::
string
*
str
)
{
safe_clear
(
reinterpret_cast

(
get_string_data
(
*
str
)
)
,
str
->
capacity
(
)
)
;
str
->
~
basic_string
(
)
;
::
operator
delete
(
str
)
;
}
}
struct
dump
{
std
::
unique_ptr

buf
;
char
*
block
;
size_t block_size
;
dump
(
std
::
string
&
str
)
{
block
=
get_string_data
(
str
)
;
block_size
=
str
.
capacity
(
)
;
buf
=
std
::
make_unique

(
block_size
)
;
std
::
copy
(
block
,
block
+
block_size
,
buf
.
get
(
)
)
;
}
static
void
print_block
(
const
char
*
dd
,
size_t size
)
{
for
(
const
char
*
p
=
dd
-
size
;
p
!=
dd
;
p
++
)
{
const
auto
c
=
*
(
p
+
size
)
;
std
::
cout

(
static_cast

(
c
)
)

using
str_seq
=
std
::
integer_sequence

;
template

constexpr
str_seq

operator
""
_s
(
)
{
return
{
}
;
}
template

struct
dump_var
;
template

struct
dump_var
>
{
static
constexpr
char
name
[
sizeof
.
.
.
(
name_c
)
+
1
]
=
{
name_c
.
.
.
,
'\0'
}
;
dump_var
(
std
::
function

f
)
{
auto
dumper
=
f
(
name
)
;
if
(
std
::
all_of
(
dumper
.
block
,
dumper
.
block
+
dumper
.
block_size
,
[
]
(
auto
v
)
{
return
v
==
0
;
}
)
)
std
::
cout

(
dumper
.
buf
.
get
(
)
)
,
dumper
.
block_size
)
;
std
::
cout

int
{
dump_var

(
[
]
(
auto
str
)
{
std
::
string
usual_way
(
str
)
;
usual_way
.
clear
(
)
;
// 
(
[
]
(
auto
str
)
{
std
::
unique_ptr
>
custom_deleter
(
new
std
::
string
(
str
)
,
secure
::
string_clear
)
;
return
dump
(
*
custom_deleter
)
;
}
)
;
}


Цитата:
Сообщение от Спойлер  


Код:





Код:
Test {Normal std::string usage blah blah blah} NOT passed!
Memory block before
0() 111(o) 114(r) 109(m) 97(a) 108(l) 32( ) 115(s) 116(t) 100(d) 58(:) 58(:) 115(s) 116(t) 114(r) 105(i) 110(n) 103(g) 32( ) 117(u) 115(s) 97(a) 103(g) 101(e) 32( ) 98(b) 108(l) 97(a) 104(h) 32( ) 98(b) 108(l) 97(a) 104(h) 32( ) 98(b) 108(l) 97(a) 104(h)
Memory block after
152(�) 43(+) 220(�) 249(�) 211(�) 127() 0() 0() 152(�) 43(+) 220(�) 249(�) 211(�) 127() 0() 0() 110(n) 103(g) 32( ) 117(u) 115(s) 97(a) 103(g) 101(e) 32( ) 98(b) 108(l) 97(a) 104(h) 32( ) 98(b) 108(l) 48(0) 0() 0() 0() 0() 0() 0()
Test {Custom deleter blah blah blah} passed!


Live: http://coliru.stacked-crooked.com/a/a08b5ab2257acbab

Think I described it in the way you needed, I mean I correctly represented a word "safest"

By the way, take a look how it's implemented in, for instance, chromium project

chromium/third_party/unrar/src/secpassword.cpp at 6efa1184771ace08f3e2162b0255c93526d1750d · chromium/chromium

The official GitHub mirror of the Chromium source. Contribute to chromium/chromium development by creating an account on GitHub.

github.com
 
Ответить с цитированием