hyperscan
hyperscan copied to clipboard
Memory leak / invalid pointer when serializing database in Windows built with Cygwin
Hello,
I believe I have found a bug when running Hyperscan on Windows. On linux I have not been able to reproduce the error. It happens when I try to free a serialized database without a custom memory allocator. When I set a custom memory allocator (which is only a malloc
and a free
) it is able to free without further problem, however without defining this custom allocator it crashes. By using DrMemory I was able to find the following memory errors:
Error #2: INVALID HEAP ARGUMENT to free 0x0000000002069f30
# 0 replace_free [D:\a\drmemory\drmemory\common\alloc_replace.c:2710]
# 1 main
Note: @0:00:07.101 in thread 18456
Note: refers to -1 byte(s) before next malloc
Note: next higher malloc: 0x0000000002069f30-0x000000000206b3e0
Note: refers to -5296 byte(s) beyond last valid byte in prior malloc
Note: prev lower malloc: 0x0000000002069f30-0x000000000206b3e0
Error #3: LEAK 5296 direct bytes 0x0000000002069f30-0x000000000206b3e0 + 0 indirect bytes
# 0 replace_malloc [D:\a\drmemory\drmemory\common\alloc_replace.c:2580]
# 1 hs_runtime.dll!hs_serialize_database+0x93 (0x00007ffa94882b44 <hs_runtime.dll+0xc2b44>)
# 2 main
(Note that Error #1
is unrelated)
It seems I am not able to free the serialized database returned by hs_serialize_database
. Nevertheless, if I set the custom allocator to the following one:
void * allocator(size_t bytes)
{
return (void *) malloc(bytes);
}
void deallocator(void * ptr)
{
free(ptr);
}
[...]
hs_err = hs_set_misc_allocator(allocator, deallocator);
if(hs_err != HS_SUCCESS)
throw std::runtime_error("ERROR: Could not use allocator");
[...]
Then no leak or invalid heap argument is reported and the program finishes correctly.
Version of the build and reproducible example
I have built Hyperscan 5.4.0 in Cygwin64 version 3.3.4(0.341/5/3), Windows 11 Enterprise version 21H2 and GCC version x86_64-w64-mingw32-g++ 11.2.0. An isolated (hopefully reproducible) example is shown below:
#include <iostream>
#include <vector>
#include <hs.h>
void * allocator(size_t bytes)
{
return (void *) malloc(bytes);
}
void deallocator(void * ptr)
{
free(ptr);
}
int main(int ac, char ** av)
{
// Vectors to hold the patterns expressions, flags and IDs
std::vector<const char *> cstr_patterns;
std::vector<unsigned> patterns_flags;
std::vector<unsigned> patterns_ids;
std::string p1 = "A random regex pattern";
std::string p2 = "value: [0-9]+";
cstr_patterns.push_back(p1.c_str());
cstr_patterns.push_back(p2.c_str());
patterns_flags.push_back(HS_FLAG_DOTALL | HS_FLAG_MULTILINE | HS_FLAG_SOM_LEFTMOST);
patterns_flags.push_back(HS_FLAG_DOTALL | HS_FLAG_MULTILINE | HS_FLAG_SOM_LEFTMOST);
patterns_ids.push_back(0);
patterns_ids.push_back(1);
// Database structures
hs_database_t * db_block = NULL;
hs_compile_error_t * compile_err = NULL;
std::cout << "Begin compilation" << std::endl;
int err = 0;
// Compile
err = hs_compile_multi(cstr_patterns.data(), patterns_flags.data(),
patterns_ids.data(), cstr_patterns.size(), HS_MODE_BLOCK,
NULL, &db_block, &compile_err);
// Check if there were any errors
if (err != HS_SUCCESS)
{
std::string error_msg;
if (compile_err->expression < 0)
error_msg.append("ERROR: "+std::string(compile_err->message)+"\n");
else
error_msg.append("ERROR: Pattern [id: "+std::string(std::to_string(compile_err->expression))+"] "+std::string(cstr_patterns[compile_err->expression])
+ " failed with error "+std::string(compile_err->message)+"\n");
hs_free_compile_error(compile_err);
throw std::runtime_error(error_msg);
}
else
hs_free_compile_error(compile_err);
std::cout << "Finished compilation" << std::endl;
// Set custom allocator
hs_error_t hs_err;
hs_err = hs_set_misc_allocator(allocator, deallocator);
if(hs_err != HS_SUCCESS)
throw std::runtime_error("ERROR: Could not use allocator");
std::cout << "Begin serialization " << std::endl;
// Serialize DB
size_t serialized_size;
char * serialized_db = NULL;
hs_err = hs_serialize_database(db_block, &serialized_db, &serialized_size);
if(hs_err != HS_SUCCESS)
throw std::runtime_error("ERROR: Could not serialize db");
std::cout << "Finished serialization " << std::endl;
// Free database
std::cout << "Freeing database " << std::endl;
hs_err = hs_free_database(db_block);
if(hs_err != HS_SUCCESS)
throw std::runtime_error("ERROR: Could not free db");
// Do something .........
// Free serialized db
std::cout << "Freeing serialized database" << std::endl;
free(serialized_db);
std::cout << "Finished" << std::endl;
return 0;
}
Also the full compilation line (on cygwin):
/bin/x86_64-w64-mingw32-g++.exe -static -g -fno-inline -fno-omit-frame-pointer -o isolated.exe -I/<path-to-hyperscan>/hyperscan/src/ -I/<path-to-hyperscan>/hyperscan/ isolated.cpp /<path-to-hyperscan>/hyperscan/bin/hs.dll /<path-to-hyperscan>/hyperscan/bin/hs_runtime.dll
In order to reproduce the error, simply comment the lines regarding the setting of the hs_set_misc_allocator
.
I believe the problem is that the pointer returned by char *out = hs_misc_alloc(length);
in database.c is not freeable by the default system free
.
Thank you for your support, Esteban