kazuho / picojson

a header-file-only, JSON parser serializer in C++
BSD 2-Clause "Simplified" License
1.12k stars 221 forks source link

Crashes parsing end of array or empty array #60

Closed JetForMe closed 9 years ago

JetForMe commented 9 years ago

The following JSON causes the code below it to fail when it gets to the end of the array. Specifically, I get a bad_alloc exception (on OS X):

Podtique(44576,0x7fff7c44d300) malloc: *** mach_vm_map(size=107202383921152) failed (error code=3)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug

This happens on the line with " <===== bad_alloc" below.

[
    {
        "freq" : 0.1,
        "desc" : "Desc1",
        "playlist" :
        [
        ]
    }
]

[
    {
        "freq" : 0.1,
        "desc" : "Desc2",
        "playlist" :
        [
            "one",
            "two"
        ]
    }
]

bool
getFromJSONIter(picojson::value::array::const_iterator& inIter, const std::string& inKey, std::string& outVal)
{
    if (!inIter->is<picojson::value::object>())
    {
        return false;
    }

    picojson::value::object obj = inIter->get<picojson::value::object>();
    const picojson::value& v = obj[inKey];
    if (!v.is<std::string>())
    {
        return false;
    }
    outVal = v.get<std::string>();

    return true;
}

bool
getFromJSONIter(picojson::value::array::const_iterator& inIter, const std::string& inKey, double& outVal)
{
    if (!inIter->is<picojson::value::object>())
    {
        return false;
    }

    picojson::value::object obj = inIter->get<picojson::value::object>();
    const picojson::value& v = obj[inKey];
    if (!v.is<double>())
    {
        return false;
    }
    outVal = v.get<double>();

    return true;
}

bool
getFromJSONIter(picojson::value::array::const_iterator& inIter, const std::string& inKey, picojson::value::array& outVal)
{
    if (!inIter->is<picojson::value::object>())
    {
        return false;
    }

    picojson::value::object obj = inIter->get<picojson::value::object>();
    const picojson::value& v = obj[inKey];
    if (!v.is<picojson::value::array>())
    {
        return false;
    }
    outVal = v.get<picojson::value::array>();

    return true;
}

bool
Spectrum::parseSpectrum(const picojson::value& inJSON)
{
    //  Top level is an array…

    if (!inJSON.is<picojson::array>())
    {
        return false;
    }

    const picojson::value::array& stations = inJSON.get<picojson::array>();
    for (picojson::value::array::const_iterator iter = stations.begin(); iter != stations.end(); ++iter)
    {
        std::string desc;
        if (!getFromJSONIter(iter, "desc", desc)) { continue; }

        double freq;
        if (!getFromJSONIter(iter, "freq", freq)) { continue; }

        picojson::value::array playlist;
        if (!getFromJSONIter(iter, "playlist", playlist)) { continue; }

        LogDebug("Station: %s, freq: %f, tracks: %lu", desc.c_str(), freq, playlist.size());

        int i = 0;
        for (picojson::value::array::const_iterator trackIter = playlist.begin(); iter != playlist.end(); ++trackIter)
        {
            if (!trackIter->is<std::string>()) { continue; }
            std::string track = trackIter->get<std::string>();      <===== bad_alloc
            LogDebug("Track %02d: %s", i++, track.c_str());
        }

    }
    return true;
}
JetForMe commented 9 years ago

Note that this doesn't happen if I iterate the array like this:

for(size_t idx = 0; idx < playlist.size(); ++idx)
{
    const picojson::value& v = playlist[idx];
    if (!v.is<std::string>()) { continue; }
    std::string track = v.get<std::string>();
    LogDebug("Track %02i: %s", idx, track.c_str());
}
JetForMe commented 9 years ago

Also, I noticed I was getting corrupted values for a few of the strings using the iterator method, but with the index approach it seems correct.

kazuho commented 9 years ago

Seems like an error in your code:

for (picojson::value::array::const_iterator trackIter = playlist.begin(); iter != playlist.end(); ++trackIter)

The condition to exit the loop should be trackIter != playlist.end().

JetForMe commented 9 years ago

Hahahahahahaa I'm such an idiot! I'm so sorry. I even looked at that line to be sure I had it right, thinking there was an issue at the end of the array. Wow. So sorry.