Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON parsing fix from steem PR 2178 #15

Merged
merged 23 commits into from
Mar 13, 2018

Conversation

pmconrad
Copy link

@pmconrad pmconrad commented Mar 1, 2018

No description provided.

@abitmore
Copy link
Member

abitmore commented Mar 1, 2018

And please add this one: EOSIO/eos@9aa838e

@abitmore
Copy link
Member

abitmore commented Mar 1, 2018

In order to apply the EOS json patch, need to remove variant json::from_stream( buffered_istream& in, parse_type ptype ), otherwise it doesn't compile.

@pmconrad
Copy link
Author

pmconrad commented Mar 3, 2018

Picked EOSIO/eos@842e12f

Wrt EOSIO/eos@9aa838e :

This patch cannot be applied, because variant_from_stream is called with different stream types which have inconsistent EOF handling. For example, fc::istream doesn't have an eof(), instead, most implementations/subclasses of fc::istream throw an eof_exception on EOF. fc::stringstream has an eof() method and throws. All streams derived from boost::asio throw and don't have an eof(). fc::ifstream has eof() and a read() method that throws and a read_some() method that doesn't, thus violating the contract of fc::istream. fc::json::from_file calls variant_from_stream with a boost::filesystem::ifstream that only has an eof().

This is a horrible mess. Investigating.

@abitmore
Copy link
Member

abitmore commented Mar 4, 2018

In EOS, before applying the patch, some code got commented out, apparently the code is not being used. Then it compiles.

@pmconrad do you prefer we merge the patch now, or merge after you finished investigation?

abitmore
abitmore previously approved these changes Mar 4, 2018
@pmconrad
Copy link
Author

pmconrad commented Mar 4, 2018

Give me some more time please. Unless you think it's a critical issue.

@pmconrad
Copy link
Author

pmconrad commented Mar 6, 2018

Fixed json::from_file, added unit tests. Please review.

It seems the graphene codebase only uses one of the configurable parsers, so there is some superfluous stuff in there.

@abitmore abitmore self-assigned this Mar 6, 2018
@pmconrad
Copy link
Author

pmconrad commented Mar 6, 2018

Fix is incomplete (json::variants_from_string uses relaxed parser)

Copy link
Member

@abitmore abitmore left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly fine. Need to fix another issue as discussed in Telegram.

default:
FC_ASSERT( false, "Unknown JSON parser type {ptype}", ("ptype", ptype) );
}
fc::istream_ptr in( new fc::ifstream( p ) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need std::ios::binary here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, need std::ios::binary in from_file(...) below as well.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ifstream always calls open with std::io::binary, see

my->ifs.open( bfp, std::ios::binary );

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Thanks. (Just noticed the 2 comments above are talking about same code).

@abitmore
Copy link
Member

abitmore commented Mar 6, 2018

By the way, in void to_stream( T& os, const variant& v, json::output_formatting format ), when handling variant::int64_type, didn't check for negative value.

@abitmore abitmore dismissed their stale review March 6, 2018 23:57

False alarm.

@abitmore abitmore assigned pmconrad and unassigned abitmore Mar 7, 2018
@pmconrad pmconrad assigned abitmore and unassigned pmconrad Mar 7, 2018
FC_THROW_EXCEPTION( parse_error_exception, "Unexpected char '${c}' in \"${s}\"",
("c", c)("s", stringFromToken(in)) );
}
case '"':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about single quotes? According to stringFromStream(...), If strict is false, need to support it and raw strings etc.

case 0:
default:
FC_THROW_EXCEPTION( parse_error_exception, "Unexpected char '${c}' in \"${s}\"",
("c", c)("s", stringFromToken(in)) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about '\'? According to other code, unquoted strings are allowed in non-strict mode.

@@ -188,18 +190,17 @@ namespace fc
continue;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So ',' can be omitted, e.g. replaced by spaces?
And allow/ignore something like , , ,,,?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticed that too. Question is, how much of the original behaviour do we want to change? (Same for other comments above.)

src/io/json.cpp Outdated
@@ -276,6 +290,7 @@ namespace fc
if (dot)
FC_THROW_EXCEPTION(parse_error_exception, "Can't parse a number with two decimal places");
dot = true;
[[fallthrough]];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This attribute is since C++17. I heard Visual Studio doesn't support it but will generate an error (I didn't verify).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. Unknown attributes are to be ignored - but only since C++17. :-/

@@ -299,16 +314,20 @@ namespace fc
}
}
catch (fc::eof_exception&)
{
{ // EOF ends the loop
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'\0' will ends the loop as well, but hasn't been handled.
E.G. if the string is "-\0[whatever]", ss.str() would be "-", so it wouldn't get caught by the "obviously wrong things" check below, finally an exception will be thrown on return to_int64(str);.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but the same is true for "-[whatever]".
The logic here is: a number is a sequence of digits 0-9, possibly separated once by a dot, and possibly prefixed with a single dash. It doesn't matter if the [whatever] sequence starts with a \0 or something else.
Will add "-" to the list of obviously wrong things.

src/io/json.cpp Outdated
if( format == json::stringify_large_ints_and_doubles &&
i > 0xffffffff )
v.as_int64() > 0xffffffff )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to quote it if v.as_int64() < -(int64_t)0xffffffff?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should.

@@ -616,27 +590,19 @@ namespace fc
os << "null";
return;
case variant::int64_type:
{
int64_t i = v.as_int64();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style, mostly.
IMO these blocks are clumsy, and they're only required so you can declare the "i" variable within the case.

@abitmore abitmore removed their assignment Mar 7, 2018
Copy link
Member

@abitmore abitmore left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

src/io/json.cpp Outdated
if( format == json::stringify_large_ints_and_doubles &&
i > 0xffffffff )
( v.as_int64() > 0xffffffff || v.as_int64() < -int64_t(0xffffffff) ) )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I think the range should be [-0x7fffffff, 0x7fffffff]

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK the reason for this is the number representation in some scripting languages like JavaScript, which is based on IEEE doubles with 52 bits of precision. 2^32-1 should be safe, as well as -(2^32-1).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The string representation will not enable bit shifting on large integers either.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The string representation will not enable bit shifting on large integers either.

True, but the point is, if it represents as a number, it should enable bit shifting, so 2^31 -1 is the max safe number.

@abitmore
Copy link
Member

abitmore commented Mar 8, 2018

@pmconrad to_stream() has recursion issue.

{
FC_ASSERT( max_depth > 0, "Too many nested objects!" );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an issue related to this, although not necessarily caused by it. For example, among this code:

object o( a_nested_object );
variant a;
try {
   to_stream( nested_object, a );
} FC_CAPTURE_AND_RETHROW ( (o) );

FC_CAPTURE_AND_RETHROW (); will try to jsonify o which is the nested object, so it will throw another exception.

We need to carefully check all catches and make sure they don't throw again.

@abitmore
Copy link
Member

@pmconrad something is broken. Unable to make witness_node. Maybe related to the code handling '$' in format_string?

[ 96%] Building CXX object libraries/egenesis/CMakeFiles/graphene_egenesis_full.dir/egenesis_full.cpp.o
/app/bts-dev/build/libraries/egenesis/egenesis_full.cpp:43:38: error: unable to find numeric literal operator ‘operator""$’
 static const char genesis_json_array[941747$][40$+1] =

@pmconrad
Copy link
Author

Sorry, didn't test that.
IMO the fault lies with libraries/egenesis/egenesis_*.cpp.tmpl - in some places they use placeholders of the form ${x}$ instead of the correct ${x}. The previous version of format_string would skip a $ char if it wasn't followed by a {, which I think is wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants