Code Monkey home page Code Monkey logo

Comments (4)

ftpronk avatar ftpronk commented on June 12, 2024

Or, if you prefer the code in text:

(I extracted rapid yaml as a single header to a file rapidyaml.h):

main.cpp

#include "yamlreader.h"

int main()
{
  YamlReader reader("./test.yaml");

  reader.printTree();

  return 0;
}

yamlreader.h

#define RYML_SINGLE_HDR_DEFINE_NOW
#include "rapidyaml.h"

#include <fstream>
#include <iostream>
#include <sstream>
#include <string>


class YamlReader
{
  using Tree = c4::yml::Tree;

public: // attributes

  std::shared_ptr<Tree> m_Tree = nullptr;

public: // methods

  YamlReader(const std::string&);

  void printTree();
};


YamlReader::YamlReader(const std::string &filename)
{
  std::string buffer;
  {
    std::ifstream file(filename);
    if (file) {
      std::stringstream ssbuf;
      ssbuf << file.rdbuf();

      buffer = ssbuf.str();
    }
  }

  if (!buffer.empty()) {
    m_Tree = std::make_shared<Tree>(ryml::parse_in_place(ryml::to_substr(buffer)));

    // Resolve anchors and references
    m_Tree->resolve();
  }
}

void YamlReader::printTree()
{
  if (!m_Tree)
    return;

  std::cerr << "\n********************" << std::endl;
  std::string key;
  for (auto child : m_Tree->rootref()) {
    std::cerr << " - entry: " << child << std::endl;
    if (child.has_key()) {
      child >> ryml::key(key);
    }
    std::cerr << " - key: " << key << std::endl << std::endl;
  }
  std::cerr << "********************" << "\n" << std::endl;
}

test.yaml

first-definition:
  type: node
  x: 5

second-definition:
  type: node
  x: 10

main_2.cpp

#define RYML_SINGLE_HDR_DEFINE_NOW
#include "rapidyaml.h"

#include <iostream>
#include <fstream>
#include <memory>
#include <string>
#include <sstream>


using Tree = c4::yml::Tree;
using namespace std;

void printOut(std::shared_ptr<Tree> tree)
{
  if (tree) {
    std::string key;

    std::cerr << "\n********************" << std::endl;

    for (auto child : tree->rootref()) {
      if (child.has_key()) {
        child >> ryml::key(key);
        std::cerr << " - key: " << key << std::endl << std::endl;
      }
      std::cerr << " - entry: " << child<< std::endl;
    }
    std::cerr << "********************" << "\n" << std::endl;
  }
}

int main()
{
  std::string buffer;
  {
    std::ifstream file("./test.yaml");
    if (file) {
      std::stringstream ssbuf;
      ssbuf << file.rdbuf();

      buffer = ssbuf.str();
    }
  }

  if (!buffer.empty()) {
    std::shared_ptr<Tree> tree = std::make_shared<Tree>(ryml::parse_in_place(ryml::to_substr(buffer)));

    tree->resolve();

    printOut(tree);
  }

  return 0;
}

from rapidyaml.

biojppm avatar biojppm commented on June 12, 2024

There's a read after free in your code:

YamlReader::YamlReader(const std::string &filename)
{
  std::string buffer; // BANG! (1)
  {
    std::ifstream file(filename);
    if (file) {
      std::stringstream ssbuf;
      ssbuf << file.rdbuf();

      buffer = ssbuf.str();
    }
  }

  if (!buffer.empty()) {
    // BANG! (2)
    m_Tree = std::make_shared<Tree>(ryml::parse_in_place(ryml::to_substr(buffer)));

    // Resolve anchors and references
    m_Tree->resolve();
  }
  // BANG! (3)
}

You are reading the file into a function-local variable buffer in (1), and you are using parse_in_place() to parse the contents in (2). This will populate the tree with pointers to buffer. But then buffer goes out of scope and is freed in (3).

So after this function returns, the tree is pointing into freed memory. Hence your read-after-free.

There are two ways to fix your code:

  • use parse_in_arena() which will copy the contents of buffer into the tree's arena before parsing the arena in place
  • OR preferably, save the copy and just make buffer a member of your class, preventing it from being freed.

Really, this is amply explained in the docs: ryml merely holds views to the YAML buffer. It is up to you -- meaning that it is both your power and your responsibility -- to decide where to store it, and then ensure the lifetimes of the buffer and tree match.

from rapidyaml.

biojppm avatar biojppm commented on June 12, 2024

There is yet another problem with your class: the tree will not be initialized when the buffer is empty. I suggest removing the if(!buffer.empty()) branch.

from rapidyaml.

ftpronk avatar ftpronk commented on June 12, 2024

I had somehow missed the parse_in_place subtility. That makes sense. Thank you.

from rapidyaml.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.