From 4215bc82eaca8d7ac361bf1aeb80045258d5a4cf Mon Sep 17 00:00:00 2001 From: Eric Salo Date: Wed, 7 Sep 2022 14:21:18 -0700 Subject: [PATCH] fix the json parser to handle floats very near overflow Instead of bounds-testing a double value to guess whether it will overflow, just convert it to a float and check whether the result is +/- infinity. PiperOrigin-RevId: 472811865 --- BUILD | 18 ++++- upb/json_decode.c | 8 +- upb/json_decode_test.cc | 93 +++++++++++++++++++++++ upb/{json_test.cc => json_encode_test.cc} | 0 upb/json_test.proto | 2 + 5 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 upb/json_decode_test.cc rename upb/{json_test.cc => json_encode_test.cc} (100%) diff --git a/BUILD b/BUILD index fced65eaf5..68cd34503e 100644 --- a/BUILD +++ b/BUILD @@ -513,8 +513,22 @@ upb_proto_reflection_library( ) cc_test( - name = "json_test", - srcs = ["upb/json_test.cc"], + name = "json_decode_test", + srcs = ["upb/json_decode_test.cc"], + deps = [ + ":json", + ":json_test_upb_proto", + ":json_test_upb_proto_reflection", + ":reflection", + ":struct_upb_proto", + ":upb", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "json_encode_test", + srcs = ["upb/json_encode_test.cc"], deps = [ ":json", ":json_test_upb_proto", diff --git a/upb/json_decode.c b/upb/json_decode.c index 7a437db166..56775cb778 100644 --- a/upb/json_decode.c +++ b/upb/json_decode.c @@ -745,11 +745,11 @@ static upb_MessageValue jsondec_double(jsondec* d, const upb_FieldDef* f) { } if (upb_FieldDef_CType(f) == kUpb_CType_Float) { - if (val.double_val != INFINITY && val.double_val != -INFINITY && - (val.double_val > FLT_MAX || val.double_val < -FLT_MAX)) { - jsondec_err(d, "Float out of range"); + float f = val.double_val; + if (val.double_val != INFINITY && val.double_val != -INFINITY) { + if (f == INFINITY || f == -INFINITY) jsondec_err(d, "Float out of range"); } - val.float_val = val.double_val; + val.float_val = f; } return val; diff --git a/upb/json_decode_test.cc b/upb/json_decode_test.cc new file mode 100644 index 0000000000..fcc65a9360 --- /dev/null +++ b/upb/json_decode_test.cc @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2009-2022, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "upb/json_decode.h" + +#include + +#include "google/protobuf/struct.upb.h" +#include "gtest/gtest.h" +#include "upb/def.hpp" +#include "upb/json_test.upb.h" +#include "upb/json_test.upbdefs.h" +#include "upb/upb.hpp" + +static upb_test_Box* JsonDecode(const char* json, upb_Arena* a) { + upb::Status status; + upb::DefPool defpool; + upb::MessageDefPtr m(upb_test_Box_getmsgdef(defpool.ptr())); + EXPECT_TRUE(m.ptr() != nullptr); + + upb_test_Box* box = upb_test_Box_new(a); + int options = 0; + bool ok = upb_JsonDecode(json, strlen(json), box, m.ptr(), defpool.ptr(), + options, a, status.ptr()); + return ok ? box : nullptr; +} + +struct FloatTest { + const std::string json; + float f; +}; + +static const std::vector FloatTestsPass = { + {R"({"f": 0})", 0}, + {R"({"f": 1})", 1}, + {R"({"f": 1.000000})", 1}, + {R"({"f": 1.5e1})", 15}, + {R"({"f": 15e-1})", 1.5}, + {R"({"f": -3.5})", -3.5}, + {R"({"f": 3.402823e38})", 3.402823e38}, + {R"({"f": -3.402823e38})", -3.402823e38}, + {R"({"f": 340282346638528859811704183484516925440.0})", + 340282346638528859811704183484516925440.0}, + {R"({"f": -340282346638528859811704183484516925440.0})", + -340282346638528859811704183484516925440.0}, +}; + +static const std::vector FloatTestsFail = { + {R"({"f": 1z})", 0}, + {R"({"f": 3.4028236e+38})", 0}, + {R"({"f": -3.4028236e+38})", 0}, +}; + +// Decode some floats. +TEST(JsonTest, DecodeFloats) { + upb::Arena a; + + for (const auto& test : FloatTestsPass) { + upb_test_Box* box = JsonDecode(test.json.c_str(), a.ptr()); + EXPECT_NE(box, nullptr); + float f = upb_test_Box_f(box); + EXPECT_EQ(f, test.f); + } + + for (const auto& test : FloatTestsFail) { + upb_test_Box* box = JsonDecode(test.json.c_str(), a.ptr()); + EXPECT_EQ(box, nullptr); + } +} diff --git a/upb/json_test.cc b/upb/json_encode_test.cc similarity index 100% rename from upb/json_test.cc rename to upb/json_encode_test.cc diff --git a/upb/json_test.proto b/upb/json_test.proto index d14b2f105a..0765333f5e 100644 --- a/upb/json_test.proto +++ b/upb/json_test.proto @@ -17,4 +17,6 @@ message Box { optional Tag last_tag = 5; optional string name = 4; optional google.protobuf.Value val = 6; + optional float f = 7; + optional double d = 8; }