c++ - Why do I need to initialize dynamically allocated array at allocation time with ()? -
i have following piece of code:
#include <cassert> #include <cstring> #include <iostream> class line { public: line(const char* c) { len = std::strlen(c); text = new char[len + 1](); std::strncpy(text, c, len); } line& operator=(const line& rhs) { if (&rhs == this) return *this; delete[] text; text = new char[rhs.getlen()](); std::strncpy(text, rhs.gettext(), rhs.getlen()); return *this; } line(const line& rhs) { len = rhs.getlen(); text = new char[rhs.getlen() + 1](); std::strncpy(text, rhs.gettext(), len); } ~line() { delete[] text; } unsigned getlen() const { return len; } char* gettext() const { return text; } private: char* text; unsigned len; }; // operator overloads const line operator*(const line& lhs, const line& rhs) { unsigned newlen = lhs.getlen() + rhs.getlen(); char* newc = new char[newlen + 1](); std::strncat(newc, lhs.gettext(), lhs.getlen()); std::strncat(newc, rhs.gettext(), rhs.getlen()); line line(newc); delete[] newc; return line; } bool operator==(const line& lhs, const line& rhs) { return strcmp(lhs.gettext(), rhs.gettext()) == 0; } std::ostream& operator<<(std::ostream& os, const line& l) { os << l.gettext(); return os; } int main(void) { line l("hello"); line l2("hello"); line r("world"); line x = l * r; assert(strcmp("hello", l.gettext()) == 0); assert(strcmp("helloworld", x.gettext()) == 0); assert(l == l2); assert(!(r == l2)); std::cout << l << '\n'; std::cout << r << '\n'; std::cout << x << '\n'; }
without parenthesis @ line:
text = new char[len + 1];
i assert @ line
assert(strcmp("helloworld", x.gettext()) == 0);
a.out: main.cpp:83: int main(): assertion `strcmp("hello", l.gettext()) == 0' failed.
and without asserts sanitizer error:
==29167==error: addresssanitizer: heap-buffer-overflow on address 0x60200000eff6 @ pc 0x7f0f48d679cb bp 0x7fffb3df3820 sp 0x7fffb3df2fd0 read of size 7 @ 0x60200000eff6 thread t0 #0 0x7f0f48d679ca in __interceptor_strlen (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x6d9ca) #1 0x7f0f48a9f938 in std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*) (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0xb2938) #2 0x40114a in operator<<(std::ostream&, line const&) /home/xxx/programming/c/xxx/main.cpp:72 #3 0x401276 in main /home/xxx/programming/c/xxx/main.cpp:88 #4 0x7f0f48432ec4 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4) #5 0x400ed8 (/home/xxx/programming/c/xxx/a.out+0x400ed8) 0x60200000eff6 located 0 bytes right of 6-byte region [0x60200000eff0,0x60200000eff6) allocated thread t0 here: #0 0x7f0f48d8f30a in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x9530a) #1 0x4014fd in line::line(char const*) /home/xxx/programming/c/xxx/main.cpp:11 #2 0x40121d in main /home/xxx/programming/c/xxx/main.cpp:78 #3 0x7f0f48432ec4 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4) summary: addresssanitizer: heap-buffer-overflow ??:0 __interceptor_strlen shadow bytes around buggy address: 0x0c047fff9da0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c047fff9db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c047fff9dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c047fff9dd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c047fff9de0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa 00 03 =>0x0c047fff9df0: fa fa fd fd fa fa 06 fa fa fa 06 fa fa fa[06]fa 0x0c047fff9e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c047fff9e10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c047fff9e20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c047fff9e30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c047fff9e40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa shadow byte legend (one shadow byte represents 8 application bytes): addressable: 00 partially addressable: 01 02 03 04 05 06 07 heap left redzone: fa heap right redzone: fb freed heap region: fd stack left redzone: f1 stack mid redzone: f2 stack right redzone: f3 stack partial redzone: f4 stack after return: f5 stack use after scope: f8 global redzone: f9 global init order: f6 poisoned user: f7 container overflow: fc array cookie: ac intra object redzone: bb asan internal: fe ==29167==aborting
why need initialize memory? (equivalent of using std::fill
)
the problem usage of std::strncat
. appends line end of current line. end of current line determined finding terminating 0 in current line. if not initialize data, end many random characters unlikely include 0 first element (therefore treated pre-existing data , cause buffer overflow).
use strncpy
/strcpy
copy first line:
unsigned newlen = lhs.getlen() + rhs.getlen(); char* newc = new char[newlen + 1]; std::strncpy(newc, lhs.gettext(), lhs.getlen() + 1); std::strncat(newc, rhs.gettext(), rhs.getlen() + 1);
Comments
Post a Comment