From: Richard Kreckel Date: Fri, 5 Apr 2024 16:34:48 +0000 (+0200) Subject: [BUGFIX] Fix crash in parser. X-Git-Url: https://www.ginac.de/ginac.git//ginac.git?p=ginac.git;a=commitdiff_plain;h=HEAD [BUGFIX] Fix crash in parser. Relying on aligned functions is non-portable. Reported by Diego Conti . --- diff --git a/ginac/parser/default_reader.cpp b/ginac/parser/default_reader.cpp index 1833bd92..defcc87a 100644 --- a/ginac/parser/default_reader.cpp +++ b/ginac/parser/default_reader.cpp @@ -69,32 +69,20 @@ private: registered_functions_hack& operator=(const registered_functions_hack&); }; -// Encode an integer into a pointer to a function. Since functions -// are aligned (the minimal alignment depends on CPU architecture) -// we can distinguish between pointers and integers. -static reader_func encode_serial_as_reader_func(unsigned serial) -{ - uintptr_t u = (uintptr_t)serial; - u = (u << 1) | (uintptr_t)1; - reader_func ptr = (reader_func)((void *)u); - return ptr; -} - const prototype_table& get_default_reader() { - using std::make_pair; static bool initialized = false; static prototype_table reader; if (!initialized) { - - reader[make_pair("sqrt", 1)] = sqrt_reader; - reader[make_pair("pow", 2)] = pow_reader; - reader[make_pair("power", 2)] = power_reader; - reader[make_pair("lst", 0)] = lst_reader; + + reader.insert({{"sqrt", 1}, reader_func(sqrt_reader)}); + reader.insert({{"pow", 2}, reader_func(pow_reader)}); + reader.insert({{"power", 2}, reader_func(power_reader)}); + reader.insert({{"lst", 0}, reader_func(lst_reader)}); unsigned serial = 0; for (auto & it : registered_functions_hack::get_registered_functions()) { - prototype proto = make_pair(it.get_name(), it.get_nparams()); - reader[proto] = encode_serial_as_reader_func(serial); + reader.insert({{it.get_name(), it.get_nparams()}, + reader_func(serial)}); ++serial; } initialized = true; @@ -104,15 +92,14 @@ const prototype_table& get_default_reader() const prototype_table& get_builtin_reader() { - using std::make_pair; static bool initialized = false; static prototype_table reader; if (!initialized) { - - reader[make_pair("sqrt", 1)] = sqrt_reader; - reader[make_pair("pow", 2)] = pow_reader; - reader[make_pair("power", 2)] = power_reader; - reader[make_pair("lst", 0)] = lst_reader; + + reader.insert({{"sqrt", 1}, reader_func(sqrt_reader)}); + reader.insert({{"pow", 2}, reader_func(pow_reader)}); + reader.insert({{"power", 2}, reader_func(power_reader)}); + reader.insert({{"lst", 0}, reader_func(lst_reader)}); enum { log, exp, @@ -146,8 +133,8 @@ const prototype_table& get_builtin_reader() auto it = registered_functions_hack::get_registered_functions().begin(); unsigned serial = 0; for ( ; serialget_name(), it->get_nparams()); - reader[proto] = encode_serial_as_reader_func(serial); + reader.insert({{it->get_name(), it->get_nparams()}, + reader_func(serial)}); } initialized = true; } diff --git a/ginac/parser/parse_context.h b/ginac/parser/parse_context.h index f24adbd6..015ca135 100644 --- a/ginac/parser/parse_context.h +++ b/ginac/parser/parse_context.h @@ -63,7 +63,21 @@ typedef std::pair prototype; * The parser uses (an associative array of) such functions to construct * (GiNaC) classes and functions from a sequence of characters. */ -typedef ex (*reader_func)(const exvector& args); +class reader_func { + enum { FUNCTION_PTR, GINAC_FUNCTION }; +public: + reader_func(ex (*func_)(const exvector& args)) + : type(FUNCTION_PTR), serial(0), func(func_) {} + reader_func(unsigned serial_) + : type(GINAC_FUNCTION), serial(serial_), func(nullptr) {} + ex operator()(const exvector& args) const; +private: + unsigned type; + unsigned serial; + ex (*func)(const exvector& args); +}; + + /** * Prototype table. diff --git a/ginac/parser/parser.cpp b/ginac/parser/parser.cpp index 52cf77b8..a7288c32 100644 --- a/ginac/parser/parser.cpp +++ b/ginac/parser/parser.cpp @@ -35,35 +35,17 @@ namespace GiNaC { -// -// Find out if ptr is a pointer to a function or a specially crafted integer. -// It's possible to distinguish between these because functions are aligned. -// Returns true if ptr is a pointer and false otherwise. -static bool decode_serial(unsigned& serial, const reader_func ptr) +ex reader_func::operator()(const exvector& args) const { - uintptr_t u = (uintptr_t)(void *)ptr; - if (u & 1) { - u >>= 1; - serial = (unsigned)u; - return false; - } - return true; -} - -// Figures out if ptr is a pointer to function or a serial of GiNaC function. -// In the former case calls that function, in the latter case constructs -// GiNaC function with corresponding serial and arguments. -static ex dispatch_reader_fcn(const reader_func ptr, const exvector& args) -{ - unsigned serial = 0; // dear gcc, could you please shut up? - bool is_ptr = decode_serial(serial, ptr); - if (is_ptr) - return ptr(args); - else + switch (type) { + case FUNCTION_PTR: + return func(args); + case GINAC_FUNCTION: return function(serial, args); + default: + abort(); + } } -// - /// identifier_expr: identifier | identifier '(' expression* ')' ex parser::parse_identifier_expr() @@ -93,16 +75,14 @@ ex parser::parse_identifier_expr() } // Eat the ')'. get_next_tok(); - prototype the_prototype = make_pair(name, args.size()); - auto reader = funcs.find(the_prototype); + auto reader = funcs.find({name, args.size()}); if (reader == funcs.end()) { Parse_error_("no function \"" << name << "\" with " << args.size() << " arguments"); } // reader->second might be a pointer to a C++ function or a specially // crafted serial of a GiNaC::function. - ex ret = dispatch_reader_fcn(reader->second, args); - return ret; + return reader->second(args); } /// paren_expr: '(' expression ')' @@ -206,7 +186,7 @@ ex parser::operator()(std::istream& input) get_next_tok(); ex ret = parse_expression(); // parse_expression() stops if it encounters an unknown token. - // This is not a bug: since the parser is recursive checking + // This is not a bug: since the parser is recursive, checking // whether the next token is valid is responsibility of the caller. // Hence make sure nothing is left in the stream: if (token != lexer::token_type::eof)