[RFC] Exception-friendly ASan annotations of std::string

Jonathan Wakely jwakely@redhat.com
Tue Jun 13 12:18:00 GMT 2017


On 13/06/17 14:14 +0300, Ivan Baravy wrote:
>
>
>On 06/12/2017 01:13 PM, Jonathan Wakely wrote:
>> On 12/06/17 11:11 +0100, Jonathan Wakely wrote:
>>> On 09/06/17 23:43 +0300, Ivan Baravy wrote:
>>>> Hello,
>>>>
>>>> I'm working on address sanitization of standard containers. The aim is
>>>> to detect and report accesses between size and capacity. The issue I
>>>> faced is related to std::length_error and std::bad_alloc exceptions. I
>>>> can get std::bad_alloc via huge fill insert into std::string. ASan adds
>>>> a memory overhead (internal structures) to user allocations and, when
>>>> enabled, it intercepts memory-related functions, failed allocations of
>>>> vital structures make it terminate the process. Thus it looks like
>>>> pointless for ASan annotations to be friendly to std::length_error and
>>>> std::bad_alloc exceptions.
>>>>
>>>> So my question is if it's acceptable to not consider std::length_error
>>>> and std::bad_alloc in address sanitization design?
>>>
>>> I'm not sure if I understand the question, sorry.
>>>
>>> ASan makes failed allocations call terminate, so you'll never get
>>> std::bad_alloc from operator new, is that right?
>
>Exactly, sorry if I was unclear about that.
>>> And you could still std::get bad_alloc from custom allocators, for
>>> example the following throws std::bad_alloc without ever attempting to
>>> allocate memory using operator new, or malloc:
>>>
>>> #include <string>
>>> #include <memory>
>>>
>>> char buf[1024];
>>> char* last = buf;
>>>
>>> template<class T>
>>> struct Alloc {
>>> using value_type = T;
>>> Alloc() { }
>>> template<typename U> Alloc(const Alloc<U>&) { }
>>>
>>> T* allocate(std::size_t n) {
>>>   void* p = last;
>>>   if (std::size_t(-1) / sizeof(T) > n)
>>>     throw std::bad_alloc();
>>>   n *= sizeof(T);
>>>   std::size_t space = 1024 - (last - buf);
>>>   if (!std::align(alignof(T), n, p, space))
>>>     throw std::bad_alloc();
>>>   last = (char*)p + n;
>>>   return (T*)p;
>>> }
>>>
>>> void deallocate(T* p, std::size_t n) {
>>>   n *= sizeof(T);
>>>   char* pc = (char*)p;
>>>   if (last == pc + n)
>>>     last = pc;
>>> }
>>>
>>> template<typename U> bool operator==(Alloc<U>) const { return true; }
>>> template<typename U> bool operator!=(Alloc<U>) const { return false; }
>>> };
>>>
>>> int main()
>>> {
>>> std::basic_string<char, std::char_traits<char>, Alloc<char>> s(1025, 'a');
>>> }
>
>ASan makes some assumptions on allocator's behavior (e.g. alignment).
>Therefore I follow design of ASan annotations in libc++ and enable
>sanitizer checks for std::allocator only. Perhaps, If I write a custom
>allocator that will meet requirements of ASan and throw bad_alloc or
>length_error 'by request', then I can test my implementation easily.
>Thank you for hints to this idea.
>
>>> But what does that have to do with std::length_error?
>>>
>>
>> If you're *only* talking about std::string and not other
>> specializations of std::basic_string, then I agree that you won't get
>> std::bad_alloc if ASan always terminates the process. But you can
>> still get std::length_error.
>
>Yes, I talk about std::string only, at least for now. I've been

It's definitely OK to ignore std::bad_alloc then.

>rethinking this last days and now agree that std::length_error is still
>to be considered. It seems I wanted a small, fast but wrong design.

Yes, this will throw a length_error without calling any allocation
function:

std::string().resize(std::string().max_size()+1);



More information about the Libstdc++ mailing list