Rework SpyInput buffer handling
SpyInput uses a fixed-size buffer and explicitly zeros memory which is good for
catching logic errors but defeats valgrind's memory tracking. Use a separate
buffer of exactly the correct size for each request. This correctly catches the
problem under valgrind:
```
==8694== Invalid read of size 2
==8694== at 0x54EFFB: utf16_iterate (utf16.c:10)
==8694== by 0x551126: ts_lexer__get_lookahead (lexer.c:54)
==8694== by 0x5515CD: ts_lexer_start (lexer.c:154)
==8694== by 0x54699F: parser(long,...)(long long) (parser.c:297)
==8694== by 0x54788A: parser__get_lookahead (parser.c:439)
==8694== by 0x54B2D3: parser__advance (parser.c:1150)
==8694== by 0x54C2AA: parser_parse (parser.c:1348)
==8694== by 0x53F063: ts_document_parse_with_options (document.c:136)
==8694== by 0x53EF43: ts_document_parse (document.c:107)
==8694== by 0x4AED11: {lambda()#1}::operator()() const::{lambda()#1}::operator()() const::{lambda()#4}::operator()() const::{lambda()#4}::operator()() const (document_test.cc:82)
==8694== by 0x4B56B6: std::_Function_handler<void (), {lambda()#1}::operator()() const::{lambda()#1}::operator()() const::{lambda()#4}::operator()() const::{lambda()#4}>::_M_invoke(std::_Any_data const&) (functional:1871)
==8694== by 0x40F8C5: std::function<void ()>::operator()() const (functional:2267)
==8694== Address 0x5d08be0 is 0 bytes inside a block of size 1 alloc'd
==8694== at 0x4C2E80F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8694== by 0x507C3E: SpyInput::read(void*, unsigned int*) (spy_input.cc:66)
==8694== by 0x55103D: ts_lexer__get_chunk (lexer.c:29)
==8694== by 0x5515B6: ts_lexer_start (lexer.c:152)
==8694== by 0x54699F: parser(long,...)(long long) (parser.c:297)
==8694== by 0x54788A: parser__get_lookahead (parser.c:439)
==8694== by 0x54B2D3: parser__advance (parser.c:1150)
==8694== by 0x54C2AA: parser_parse (parser.c:1348)
==8694== by 0x53F063: ts_document_parse_with_options (document.c:136)
==8694== by 0x53EF43: ts_document_parse (document.c:107)
==8694== by 0x4AED11: {lambda()#1}::operator()() const::{lambda()#1}::operator()() const::{lambda()#4}::operator()() const::{lambda()#4}::operator()() const (document_test.cc:82)
==8694== by 0x4B56B6: std::_Function_handler<void (), {lambda()#1}::operator()() const::{lambda()#1}::operator()() const::{lambda()#4}::operator()() const::{lambda()#4}>::_M_invoke(std::_Any_data const&) (functional:1871)
```
This commit is contained in:
parent
e7662c2213
commit
52cec9ed39
4 changed files with 22 additions and 25 deletions
|
|
@ -12,8 +12,7 @@ static const size_t UTF8_MAX_CHAR_SIZE = 4;
|
|||
|
||||
SpyInput::SpyInput(string content, size_t chars_per_chunk) :
|
||||
chars_per_chunk(chars_per_chunk),
|
||||
buffer_size(UTF8_MAX_CHAR_SIZE * chars_per_chunk),
|
||||
buffer(new char[buffer_size]),
|
||||
buffer(nullptr),
|
||||
byte_offset(0),
|
||||
content(content),
|
||||
encoding(TSInputEncodingUTF8),
|
||||
|
|
@ -57,12 +56,19 @@ const char * SpyInput::read(void *payload, uint32_t *bytes_read) {
|
|||
* This class stores its entire `content` in a contiguous buffer, but we want
|
||||
* to ensure that the code under test cannot accidentally read more than
|
||||
* `*bytes_read` bytes past the returned pointer. To make sure that this type
|
||||
* of error does not fly, we copy the chunk into a zeroed-out buffer and
|
||||
* of error does not fly, we allocate a separate buffer for each request and
|
||||
* return a reference to that buffer, rather than a pointer into the main
|
||||
* content.
|
||||
* content. The temporary buffer only fits `*bytes_read` bytes so valgrind
|
||||
* can detect code reading too many bytes from the buffer.
|
||||
*/
|
||||
memset(spy->buffer, 0, spy->buffer_size);
|
||||
memcpy(spy->buffer, result.data(), byte_count);
|
||||
delete[] spy->buffer;
|
||||
if (byte_count) {
|
||||
spy->buffer = new char[byte_count];
|
||||
memcpy(spy->buffer, result.data(), byte_count);
|
||||
} else {
|
||||
spy->buffer = nullptr;
|
||||
}
|
||||
|
||||
return spy->buffer;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ struct SpyInputEdit {
|
|||
|
||||
class SpyInput {
|
||||
uint32_t chars_per_chunk;
|
||||
uint32_t buffer_size;
|
||||
char *buffer;
|
||||
uint32_t byte_offset;
|
||||
std::vector<SpyInputEdit> undo_stack;
|
||||
|
|
|
|||
|
|
@ -72,6 +72,16 @@ describe("Document", [&]() {
|
|||
"(array (true) (false))");
|
||||
});
|
||||
|
||||
it("handles truncated UTF16 data", [&]() {
|
||||
const char content[1] = { '\0' };
|
||||
spy_input->content = string(content, sizeof(content));
|
||||
spy_input->encoding = TSInputEncodingUTF16;
|
||||
|
||||
ts_document_set_input(document, spy_input->input());
|
||||
ts_document_invalidate(document);
|
||||
ts_document_parse(document);
|
||||
});
|
||||
|
||||
it("allows columns to be measured in either bytes or characters", [&]() {
|
||||
const char16_t content[] = u"[true, false]";
|
||||
spy_input->content = string((const char *)content, sizeof(content));
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
#include "test_helper.h"
|
||||
#include "runtime/utf16.h"
|
||||
|
||||
START_TEST
|
||||
|
||||
describe("Lexer", [&]() {
|
||||
it("handles truncated UTF16 data", [&]() {
|
||||
uint8_t *content = new uint8_t[1];
|
||||
*content = 'A';
|
||||
|
||||
int32_t code_point = 0;
|
||||
utf16_iterate(content, 1, &code_point);
|
||||
|
||||
delete[] content;
|
||||
});
|
||||
});
|
||||
|
||||
END_TEST
|
||||
Loading…
Add table
Add a link
Reference in a new issue