<commit_before>/********************************************************************
 * Copyright © 2016 Computational Molecular Biology Group,          *
 *                  Freie Universität Berlin (GER)                  *
 *                                                                  *
 * This file is part of ReaDDy.                                     *
 *                                                                  *
 * ReaDDy is free software: you can redistribute it and/or modify   *
 * it under the terms of the GNU Lesser General Public License as   *
 * published by the Free Software Foundation, either version 3 of   *
 * the License, or (at your option) any later version.              *
 *                                                                  *
 * This program is distributed in the hope that it will be useful,  *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
 * GNU Lesser General Public License for more details.              *
 *                                                                  *
 * You should have received a copy of the GNU Lesser General        *
 * Public License along with this program. If not, see              *
 * <http://www.gnu.org/licenses/>.                                  *
 ********************************************************************/

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>

#include <readdy/api/Simulation.h>
#include <readdy/io/File.h>
#include <readdy/common/nodelete.h>
#include <readdy/api/PotentialConfiguration.h>
#include "ExportSchemeApi.h"
#include "PyPotential.h"
#include "ExportObservables.h"

namespace py = pybind11;

using rvp = py::return_value_policy;
using sim = readdy::Simulation;
using kp = readdy::plugin::KernelProvider;
using vec = readdy::model::Vec3;
using pot2 = readdy::rpy::PotentialOrder2Wrapper;
using model = readdy::model::KernelStateModel;
using ctx = readdy::model::KernelContext;
using kern = readdy::model::Kernel;

void exportTopologies(py::module &);

// thin wrappers
void setBoxSize(sim &self, const vec &size) { /* explicitly choose void(vec) signature */ self.setBoxSize(size); }

std::string getSelectedKernelType(sim &self) { /* discard const reference */ return self.getSelectedKernelType(); }

void addParticle(sim &self, const std::string &type, const vec &pos) { self.addParticle(type, pos[0], pos[1], pos[2]); }

enum class ParticleTypeFlavor {
    NORMAL = 0, TOPOLOGY = 1, MEMBRANE = 2
};

void exportApi(py::module &api) {
    using namespace pybind11::literals;
    exportSchemeApi<readdy::api::ReaDDyScheme>(api, "ReaDDyScheme");
    exportSchemeApi<readdy::api::AdvancedScheme>(api, "AdvancedScheme");

    auto topologyModule = api.def_submodule("top");
    exportTopologies(topologyModule);

    py::enum_<ParticleTypeFlavor>(api, "ParticleTypeFlavor")
            .value("NORMAL", ParticleTypeFlavor::NORMAL)
            .value("TOPOLOGY", ParticleTypeFlavor::TOPOLOGY)
            .value("MEMBRANE", ParticleTypeFlavor::MEMBRANE);

    py::enum_<readdy::api::BondType>(api, "BondType").value("HARMONIC", readdy::api::BondType::HARMONIC);
    py::enum_<readdy::api::AngleType>(api, "AngleType").value("HARMONIC", readdy::api::AngleType::HARMONIC);
    py::enum_<readdy::api::TorsionType>(api, "TorsionType").value("COS_DIHEDRAL", readdy::api::TorsionType::COS_DIHEDRAL);

    py::class_<sim> simulation(api, "Simulation");
    simulation.def(py::init<>())
            .def_property("kbt", &sim::getKBT, &sim::setKBT)
            .def_property("periodic_boundary", &sim::getPeriodicBoundary, &sim::setPeriodicBoundary)
            .def_property("box_size", &sim::getBoxSize, &setBoxSize)
            .def_property_readonly("single_precision", &sim::singlePrecision)
            .def_property_readonly("double_precision", &sim::doublePrecision)
            .def("set_expected_max_n_particles", &sim::setExpectedMaxNParticles, "n"_a)
            .def("register_particle_type",
                 [](sim &self, const std::string &name, readdy::scalar diffusionCoefficient, readdy::scalar radius,
                    ParticleTypeFlavor flavor) {
                     readdy::model::Particle::flavor_t f = [=] {
                         switch (flavor) {
                             case ParticleTypeFlavor::NORMAL:
                                 return readdy::model::Particle::FLAVOR_NORMAL;
                             case ParticleTypeFlavor::MEMBRANE:
                                 return readdy::model::Particle::FLAVOR_MEMBRANE;
                             case ParticleTypeFlavor::TOPOLOGY:
                                 return readdy::model::Particle::FLAVOR_TOPOLOGY;
                         }
                     }();
                     return self.registerParticleType(name, diffusionCoefficient, radius, f);
                 }, "name"_a, "diffusion_coefficient"_a, "radius"_a, "flavor"_a = ParticleTypeFlavor::NORMAL)
            .def("add_particle", [](sim &self, const std::string &type, const vec &pos) {
                self.addParticle(type, pos[0], pos[1], pos[2]);
            }, "type"_a, "pos"_a)
            .def("is_kernel_selected", &sim::isKernelSelected)
            .def("get_selected_kernel_type", &getSelectedKernelType)
            .def("register_potential_order_1", &sim::registerPotentialOrder1, "potential"_a)
            .def("register_potential_order_2", &sim::registerPotentialOrder2, "potential"_a)
            .def("register_potential_harmonic_repulsion", &sim::registerHarmonicRepulsionPotential,
                 "type_a"_a, "type_b"_a, "force_constant"_a)
            .def("register_potential_piecewise_weak_interaction",
                 &sim::registerWeakInteractionPiecewiseHarmonicPotential, "type_a"_a, "type_b"_a, "force_constant"_a,
                 "desired_particle_distance"_a, "depth"_a, "no_interaction_distance"_a)
            .def("register_potential_box", &sim::registerBoxPotential, "particle_type"_a, "force_constant"_a,
                 "origin"_a, "extent"_a, "consider_particle_radius"_a)
            .def("register_potential_sphere_in", &sim::registerSphereInPotential, "particle_type"_a, "force_constant"_a,
                 "origin"_a, "radius"_a)
            .def("register_potential_sphere_out", &sim::registerSphereOutPotential, "particle_type"_a,
                 "force_constant"_a, "origin"_a, "radius"_a)
            .def("register_potential_spherical_barrier", &sim::registerSphericalBarrier, "particle_type"_a, "origin"_a,
                 "radius"_a, "height"_a, "width"_a)
            .def("register_potential_lennard_jones", &sim::registerLennardJonesPotential, "particle_type_a"_a,
                 "particle_type_b"_a, "exponent_m"_a, "exponent_n"_a, "cutoff"_a, "shift"_a, "epsilon"_a, "sigma"_a)
            .def("register_potential_screened_electrostatics", &sim::registerScreenedElectrostaticsPotential,
                 "particle_type_a"_a, "particle_type_b"_a, "electrostatic_strength"_a, "inverse_screening_depth"_a,
                 "repulsion_strength"_a, "repulsion_distance"_a, "exponent"_a, "cutoff"_a)
            .def("get_particle_positions", &sim::getParticlePositions)
            .def("register_reaction_conversion", &sim::registerConversionReaction, rvp::reference_internal,
                 "label"_a, "from_type"_a, "to_type"_a, "rate"_a)
            .def("register_reaction_enzymatic", &sim::registerEnzymaticReaction, rvp::reference_internal,
                 "label"_a, "catalyst_type"_a, "from_type"_a, "to_type"_a, "rate"_a, "educt_distance"_a)
            .def("register_reaction_fission", &sim::registerFissionReaction, rvp::reference_internal,
                 "label"_a, "from_type"_a, "to_type1"_a, "to_type2"_a, "rate"_a, "product_distance"_a,
                 "weight1"_a = .5, "weight2"_a = .5)
            .def("register_reaction_fusion", &sim::registerFusionReaction, rvp::reference_internal, "label"_a,
                 "from_type1"_a, "from_type2"_a, "to_type"_a, "rate"_a, "educt_distance"_a,
                 "weight1"_a = .5, "weight2"_a = .5)
            .def("register_reaction_decay", &sim::registerDecayReaction, rvp::reference_internal,
                 "label"_a, "particle_type"_a, "rate"_a)
            .def("register_compartment_sphere", &sim::registerCompartmentSphere,
                 "conversion_map"_a, "name"_a, "origin"_a, "radius"_a, "larger_or_less"_a)
            .def("register_compartment_plane", &sim::registerCompartmentPlane, "conversion_map"_a, "name"_a,
                 "normal_coefficients"_a, "distance_from_plane"_a, "larger_or_less"_a)
            .def("get_recommended_time_step", &sim::getRecommendedTimeStep, "n"_a)
            .def("kernel_supports_topologies", &sim::kernelSupportsTopologies)
            .def("create_topology_particle", &sim::createTopologyParticle, "type"_a, "position"_a)
            .def("configure_topology_bond_potential", &sim::configureTopologyBondPotential, "type1"_a, "type2"_a,
                 "force_constant"_a, "length"_a, "type"_a = readdy::api::BondType::HARMONIC)
            .def("configure_topology_angle_potential", &sim::configureTopologyAnglePotential, "type1"_a, "type2"_a,
                 "type3"_a, "force_constant"_a, "equilibrium_angle"_a, "type"_a = readdy::api::AngleType::HARMONIC)
            .def("configure_topology_dihedral_potential", &sim::configureTopologyTorsionPotential, "type1"_a,
                 "type2"_a, "type3"_a, "type4"_a, "force_constant"_a, "multiplicity"_a, "phi_0"_a,
                 "type"_a = readdy::api::TorsionType::COS_DIHEDRAL)
            .def("get_particles_for_topology", &sim::getParticlesForTopology, "topology"_a)
            .def("add_topology", &sim::addTopology, rvp::reference, "particles"_a, "labels"_a = std::vector<std::string>())
            .def("current_topologies", &sim::currentTopologies)
            .def("set_kernel", &sim::setKernel, "name"_a)
            .def("run_scheme_readdy", [](sim &self, bool defaults) {
                     return std::make_unique<readdy::api::SchemeConfigurator<readdy::api::ReaDDyScheme>>(
                             self.runScheme<readdy::api::ReaDDyScheme>(defaults)
                     );
                 }, "defaults"_a = true
            )
            .def("run_scheme_advanced", [](sim &self, bool defaults) {
                     return std::make_unique<readdy::api::SchemeConfigurator<readdy::api::AdvancedScheme>>(
                             self.runScheme<readdy::api::AdvancedScheme>(defaults)
                     );
                 }, "defaults"_a = true
            )
            .def("run", [](sim &self, const readdy::time_step_type steps, const readdy::scalar timeStep) {
                py::gil_scoped_release release;
                self.run(steps, timeStep);
            }, "n_steps"_a, "time_step"_a);
    exportObservables(api, simulation);

    py::class_<kp, std::unique_ptr<kp, readdy::util::nodelete>>(api, "KernelProvider")
            .def_static("get", &kp::getInstance, rvp::reference)
            .def("load_from_dir", &kp::loadKernelsFromDirectory, "directory"_a);

    py::class_<pot2>(api, "Pot2")
            .def(py::init<std::string, std::string, py::object, py::object>())
            .def("calc_energy", &pot2::calculateEnergy, "x_ij"_a)
            .def("calc_force", &pot2::calculateForce, "force"_a, "x_ij"_a);

    py::class_<kern>(api, "Kernel").def("get_name", &kern::getName, rvp::reference);
}
<commit_msg>fix prototyping<commit_after>/********************************************************************
 * Copyright © 2016 Computational Molecular Biology Group,          *
 *                  Freie Universität Berlin (GER)                  *
 *                                                                  *
 * This file is part of ReaDDy.                                     *
 *                                                                  *
 * ReaDDy is free software: you can redistribute it and/or modify   *
 * it under the terms of the GNU Lesser General Public License as   *
 * published by the Free Software Foundation, either version 3 of   *
 * the License, or (at your option) any later version.              *
 *                                                                  *
 * This program is distributed in the hope that it will be useful,  *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
 * GNU Lesser General Public License for more details.              *
 *                                                                  *
 * You should have received a copy of the GNU Lesser General        *
 * Public License along with this program. If not, see              *
 * <http://www.gnu.org/licenses/>.                                  *
 ********************************************************************/

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>

#include <readdy/api/Simulation.h>
#include <readdy/io/File.h>
#include <readdy/common/nodelete.h>
#include <readdy/api/PotentialConfiguration.h>
#include "ExportSchemeApi.h"
#include "PyPotential.h"
#include "ExportObservables.h"

namespace py = pybind11;

using rvp = py::return_value_policy;
using sim = readdy::Simulation;
using kp = readdy::plugin::KernelProvider;
using vec = readdy::model::Vec3;
using pot2 = readdy::rpy::PotentialOrder2Wrapper;
using model = readdy::model::KernelStateModel;
using ctx = readdy::model::KernelContext;
using kern = readdy::model::Kernel;

void exportTopologies(py::module &);

void registerPotentialOrder2(sim &self, pot2 *potential) {
    self.registerPotentialOrder2(potential);
}

// thin wrappers
void setBoxSize(sim &self, const vec &size) { /* explicitly choose void(vec) signature */ self.setBoxSize(size); }

std::string getSelectedKernelType(sim &self) { /* discard const reference */ return self.getSelectedKernelType(); }

void addParticle(sim &self, const std::string &type, const vec &pos) { self.addParticle(type, pos[0], pos[1], pos[2]); }

enum class ParticleTypeFlavor {
    NORMAL = 0, TOPOLOGY = 1, MEMBRANE = 2
};

void exportApi(py::module &api) {
    using namespace pybind11::literals;
    exportSchemeApi<readdy::api::ReaDDyScheme>(api, "ReaDDyScheme");
    exportSchemeApi<readdy::api::AdvancedScheme>(api, "AdvancedScheme");

    auto topologyModule = api.def_submodule("top");
    exportTopologies(topologyModule);

    py::enum_<ParticleTypeFlavor>(api, "ParticleTypeFlavor")
            .value("NORMAL", ParticleTypeFlavor::NORMAL)
            .value("TOPOLOGY", ParticleTypeFlavor::TOPOLOGY)
            .value("MEMBRANE", ParticleTypeFlavor::MEMBRANE);

    py::enum_<readdy::api::BondType>(api, "BondType").value("HARMONIC", readdy::api::BondType::HARMONIC);
    py::enum_<readdy::api::AngleType>(api, "AngleType").value("HARMONIC", readdy::api::AngleType::HARMONIC);
    py::enum_<readdy::api::TorsionType>(api, "TorsionType").value("COS_DIHEDRAL", readdy::api::TorsionType::COS_DIHEDRAL);

    py::class_<sim> simulation(api, "Simulation");
    simulation.def(py::init<>())
            .def_property("kbt", &sim::getKBT, &sim::setKBT)
            .def_property("periodic_boundary", &sim::getPeriodicBoundary, &sim::setPeriodicBoundary)
            .def_property("box_size", &sim::getBoxSize, &setBoxSize)
            .def_property_readonly("single_precision", &sim::singlePrecision)
            .def_property_readonly("double_precision", &sim::doublePrecision)
            .def("set_expected_max_n_particles", &sim::setExpectedMaxNParticles, "n"_a)
            .def("register_particle_type",
                 [](sim &self, const std::string &name, readdy::scalar diffusionCoefficient, readdy::scalar radius,
                    ParticleTypeFlavor flavor) {
                     readdy::model::Particle::flavor_t f = [=] {
                         switch (flavor) {
                             case ParticleTypeFlavor::NORMAL:
                                 return readdy::model::Particle::FLAVOR_NORMAL;
                             case ParticleTypeFlavor::MEMBRANE:
                                 return readdy::model::Particle::FLAVOR_MEMBRANE;
                             case ParticleTypeFlavor::TOPOLOGY:
                                 return readdy::model::Particle::FLAVOR_TOPOLOGY;
                         }
                     }();
                     return self.registerParticleType(name, diffusionCoefficient, radius, f);
                 }, "name"_a, "diffusion_coefficient"_a, "radius"_a, "flavor"_a = ParticleTypeFlavor::NORMAL)
            .def("add_particle", [](sim &self, const std::string &type, const vec &pos) {
                self.addParticle(type, pos[0], pos[1], pos[2]);
            }, "type"_a, "pos"_a)
            .def("is_kernel_selected", &sim::isKernelSelected)
            .def("get_selected_kernel_type", &getSelectedKernelType)
            .def("register_potential_order_2", &registerPotentialOrder2, "potential"_a)
            .def("register_potential_harmonic_repulsion", &sim::registerHarmonicRepulsionPotential,
                 "type_a"_a, "type_b"_a, "force_constant"_a)
            .def("register_potential_piecewise_weak_interaction",
                 &sim::registerWeakInteractionPiecewiseHarmonicPotential, "type_a"_a, "type_b"_a, "force_constant"_a,
                 "desired_particle_distance"_a, "depth"_a, "no_interaction_distance"_a)
            .def("register_potential_box", &sim::registerBoxPotential, "particle_type"_a, "force_constant"_a,
                 "origin"_a, "extent"_a, "consider_particle_radius"_a)
            .def("register_potential_sphere_in", &sim::registerSphereInPotential, "particle_type"_a, "force_constant"_a,
                 "origin"_a, "radius"_a)
            .def("register_potential_sphere_out", &sim::registerSphereOutPotential, "particle_type"_a,
                 "force_constant"_a, "origin"_a, "radius"_a)
            .def("register_potential_spherical_barrier", &sim::registerSphericalBarrier, "particle_type"_a, "origin"_a,
                 "radius"_a, "height"_a, "width"_a)
            .def("register_potential_lennard_jones", &sim::registerLennardJonesPotential, "particle_type_a"_a,
                 "particle_type_b"_a, "exponent_m"_a, "exponent_n"_a, "cutoff"_a, "shift"_a, "epsilon"_a, "sigma"_a)
            .def("register_potential_screened_electrostatics", &sim::registerScreenedElectrostaticsPotential,
                 "particle_type_a"_a, "particle_type_b"_a, "electrostatic_strength"_a, "inverse_screening_depth"_a,
                 "repulsion_strength"_a, "repulsion_distance"_a, "exponent"_a, "cutoff"_a)
            .def("get_particle_positions", &sim::getParticlePositions)
            .def("register_reaction_conversion", &sim::registerConversionReaction, rvp::reference_internal,
                 "label"_a, "from_type"_a, "to_type"_a, "rate"_a)
            .def("register_reaction_enzymatic", &sim::registerEnzymaticReaction, rvp::reference_internal,
                 "label"_a, "catalyst_type"_a, "from_type"_a, "to_type"_a, "rate"_a, "educt_distance"_a)
            .def("register_reaction_fission", &sim::registerFissionReaction, rvp::reference_internal,
                 "label"_a, "from_type"_a, "to_type1"_a, "to_type2"_a, "rate"_a, "product_distance"_a,
                 "weight1"_a = .5, "weight2"_a = .5)
            .def("register_reaction_fusion", &sim::registerFusionReaction, rvp::reference_internal, "label"_a,
                 "from_type1"_a, "from_type2"_a, "to_type"_a, "rate"_a, "educt_distance"_a,
                 "weight1"_a = .5, "weight2"_a = .5)
            .def("register_reaction_decay", &sim::registerDecayReaction, rvp::reference_internal,
                 "label"_a, "particle_type"_a, "rate"_a)
            .def("register_compartment_sphere", &sim::registerCompartmentSphere,
                 "conversion_map"_a, "name"_a, "origin"_a, "radius"_a, "larger_or_less"_a)
            .def("register_compartment_plane", &sim::registerCompartmentPlane, "conversion_map"_a, "name"_a,
                 "normal_coefficients"_a, "distance_from_plane"_a, "larger_or_less"_a)
            .def("get_recommended_time_step", &sim::getRecommendedTimeStep, "n"_a)
            .def("kernel_supports_topologies", &sim::kernelSupportsTopologies)
            .def("create_topology_particle", &sim::createTopologyParticle, "type"_a, "position"_a)
            .def("configure_topology_bond_potential", &sim::configureTopologyBondPotential, "type1"_a, "type2"_a,
                 "force_constant"_a, "length"_a, "type"_a = readdy::api::BondType::HARMONIC)
            .def("configure_topology_angle_potential", &sim::configureTopologyAnglePotential, "type1"_a, "type2"_a,
                 "type3"_a, "force_constant"_a, "equilibrium_angle"_a, "type"_a = readdy::api::AngleType::HARMONIC)
            .def("configure_topology_dihedral_potential", &sim::configureTopologyTorsionPotential, "type1"_a,
                 "type2"_a, "type3"_a, "type4"_a, "force_constant"_a, "multiplicity"_a, "phi_0"_a,
                 "type"_a = readdy::api::TorsionType::COS_DIHEDRAL)
            .def("get_particles_for_topology", &sim::getParticlesForTopology, "topology"_a)
            .def("add_topology", &sim::addTopology, rvp::reference, "particles"_a, "labels"_a = std::vector<std::string>())
            .def("current_topologies", &sim::currentTopologies)
            .def("set_kernel", &sim::setKernel, "name"_a)
            .def("run_scheme_readdy", [](sim &self, bool defaults) {
                     return std::make_unique<readdy::api::SchemeConfigurator<readdy::api::ReaDDyScheme>>(
                             self.runScheme<readdy::api::ReaDDyScheme>(defaults)
                     );
                 }, "defaults"_a = true
            )
            .def("run_scheme_advanced", [](sim &self, bool defaults) {
                     return std::make_unique<readdy::api::SchemeConfigurator<readdy::api::AdvancedScheme>>(
                             self.runScheme<readdy::api::AdvancedScheme>(defaults)
                     );
                 }, "defaults"_a = true
            )
            .def("run", [](sim &self, const readdy::time_step_type steps, const readdy::scalar timeStep) {
                py::gil_scoped_release release;
                self.run(steps, timeStep);
            }, "n_steps"_a, "time_step"_a);
    exportObservables(api, simulation);

    py::class_<kp, std::unique_ptr<kp, readdy::util::nodelete>>(api, "KernelProvider")
            .def_static("get", &kp::getInstance, rvp::reference)
            .def("load_from_dir", &kp::loadKernelsFromDirectory, "directory"_a);

    py::class_<pot2>(api, "Pot2")
            .def(py::init<std::string, std::string, py::object, py::object>())
            .def("calc_energy", &pot2::calculateEnergy, "x_ij"_a)
            .def("calc_force", &pot2::calculateForce, "force"_a, "x_ij"_a);

    py::class_<kern>(api, "Kernel").def("get_name", &kern::getName, rvp::reference);
}
<|endoftext|>


<commit_before>#ifndef ARABICA_XSLT_OUTPUT_HANDLER_HPP
#define ARABICA_XSLT_OUTPUT_HANDLER_HPP

#include "xslt_value_validation.hpp"

namespace Arabica
{
namespace XSLT
{

class OutputHandler : public SAX::DefaultHandler<std::string>
{
public:
  OutputHandler(CompilationContext& context) :
    context_(context)
  {
  } // OutputHandler

  virtual void startElement(const std::string& namespaceURI,
                            const std::string& localName,
                            const std::string& qName,
                            const SAX::Attributes<std::string>& atts)
  {
    if(settings_.empty())
    {
      static const char* AllowedMethods[] = { "xml", "html", "text", 0 };
      static const ValueRule rules[] = { { "method", false, "xml", AllowedMethods },
                                         { "version", false, "1.0" },
                                         { "encoding", false, "UTF-8" },
                                         { "omit-xml-declaration", false, No, AllowedYesNo },
                                         { "standalone", false, "", AllowedYesNo },
                                         { "doctype-public", false, ""},
                                         { "doctype-system", false, ""},
                                         { "cdata-section-elements", false, ""},
                                         { "indent", false, No, AllowedYesNo },
                                         { "media-type", false, "" },
                                         { 0, false, 0 } };
      settings_ = gatherAttributes(qName, atts, rules);
      cdataElements_ = extractCDATAElements(settings_["cdata-section-elements"]);

      return;
    } // if(settings_ == 0)

    throw SAX::SAXException(qName + " can not contain elements");
  } // startElement

  virtual void endElement(const std::string& namespaceURI,
                          const std::string& localName,
                          const std::string& qName)
  {
    context_.stylesheet().output_settings(settings_, cdataElements_);
    context_.pop();
  } // endElement

  virtual void characters(const std::string& ch)
  {
    for(std::string::const_iterator i = ch.begin(), e = ch.end(); i != e; ++i)
      if(!Arabica::XML::is_space(*i))
        throw SAX::SAXException("xsl:output element must be empty");
  } // characters

private:
  Output::CDATAElements extractCDATAElements(const std::string& cdata_section_elements) const
  {
    Output::CDATAElements elements;

    if(cdata_section_elements.empty())
      return elements;

    std::istringstream is(cdata_section_elements);
    while(!is.eof())
    {
      std::string e;
      is >> e;

      XML::QualifiedName<std::string> qualifiedName = context_.processElementQName(e);
      elements.insert(QName::create(qualifiedName));
    } // while

    return elements;
  } // extractCDATAElements

  CompilationContext& context_;
  Output::Settings settings_;
  Output::CDATAElements cdataElements_;
}; // class OutputHandler

} // namespace XSLT
} // namespace Arabica

#endif
<commit_msg>normalize whitespace when extracting CDATA element names<commit_after>#ifndef ARABICA_XSLT_OUTPUT_HANDLER_HPP
#define ARABICA_XSLT_OUTPUT_HANDLER_HPP

#include "xslt_value_validation.hpp"

namespace Arabica
{
namespace XSLT
{

class OutputHandler : public SAX::DefaultHandler<std::string>
{
public:
  OutputHandler(CompilationContext& context) :
    context_(context)
  {
  } // OutputHandler

  virtual void startElement(const std::string& namespaceURI,
                            const std::string& localName,
                            const std::string& qName,
                            const SAX::Attributes<std::string>& atts)
  {
    if(settings_.empty())
    {
      static const char* AllowedMethods[] = { "xml", "html", "text", 0 };
      static const ValueRule rules[] = { { "method", false, "xml", AllowedMethods },
                                         { "version", false, "1.0" },
                                         { "encoding", false, "UTF-8" },
                                         { "omit-xml-declaration", false, No, AllowedYesNo },
                                         { "standalone", false, "", AllowedYesNo },
                                         { "doctype-public", false, ""},
                                         { "doctype-system", false, ""},
                                         { "cdata-section-elements", false, ""},
                                         { "indent", false, No, AllowedYesNo },
                                         { "media-type", false, "" },
                                         { 0, false, 0 } };
      settings_ = gatherAttributes(qName, atts, rules);
      cdataElements_ = extractCDATAElements(settings_["cdata-section-elements"]);

      return;
    } // if(settings_ == 0)

    throw SAX::SAXException(qName + " can not contain elements");
  } // startElement

  virtual void endElement(const std::string& namespaceURI,
                          const std::string& localName,
                          const std::string& qName)
  {
    context_.stylesheet().output_settings(settings_, cdataElements_);
    context_.pop();
  } // endElement

  virtual void characters(const std::string& ch)
  {
    for(std::string::const_iterator i = ch.begin(), e = ch.end(); i != e; ++i)
      if(!Arabica::XML::is_space(*i))
        throw SAX::SAXException("xsl:output element must be empty");
  } // characters

private:
  Output::CDATAElements extractCDATAElements(const std::string& cdata_section_elements) const
  {
    Output::CDATAElements elements;

    if(cdata_section_elements.empty())
      return elements;

    std::istringstream is(text::normalize_whitespace<std::string, Arabica::default_string_adaptor<std::string> >(cdata_section_elements));
    while(!is.eof())
    {
      std::string e;
      is >> e;

      XML::QualifiedName<std::string> qualifiedName = context_.processElementQName(e);
      elements.insert(QName::create(qualifiedName));
    } // while

    return elements;
  } // extractCDATAElements

  CompilationContext& context_;
  Output::Settings settings_;
  Output::CDATAElements cdataElements_;
}; // class OutputHandler

} // namespace XSLT
} // namespace Arabica

#endif
<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/bootloader/bl_console.C $                                 */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2019                             */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

/**
 *  @file bl_console.C
 *
 *  @brief Bootloader console functions to write data to lpc console
 */

#include <bootloader/bl_console.H>
#include <sys/task.h>
#include <sys/time.h>
#include <sys/syscall.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <lpc/lpc_const.H>
#include <uart.H>
#include <uartconfig.C>
#include <p10_sbe_hb_structures.H>
#include <util/align.H>
#include <arch/ppc.H>
#include <kernel/timemgr.H>

using namespace CONSOLE;
using namespace UARTREGS;

bool bl_console::console_fail = false;

void computeOpbmErrSev(OpbmErrReg_t i_opbmErrData,
                       ResetLevels &o_resetLevel)
{
    o_resetLevel = RESET_CLEAR;

    // First check the soft errors
    if (i_opbmErrData.rxits || i_opbmErrData.rxicmd || i_opbmErrData.rxiaa ||
        i_opbmErrData.rxopbe || i_opbmErrData.rxicmdb || i_opbmErrData.rxidatab)
    {
        o_resetLevel = RESET_OPB_LPCHC_SOFT;
    }

    // Now look for hard errors that will override soft errors reset level
    if (i_opbmErrData.rxopbt || i_opbmErrData.rxiaddr || i_opbmErrData.rxctgtel)
    {
        o_resetLevel = RESET_OPB_LPCHC_HARD;
    }
}

void computeLpchcErrSev(LpchcErrReg_t i_lpchcErrData,
                        ResetLevels &o_resetLevel)
{
    o_resetLevel = RESET_CLEAR;

    // First check the soft errors
    // All of these errors are set from bad LPC end points. Setting all to soft
    if (i_lpchcErrData.lreset || i_lpchcErrData.syncab ||
        i_lpchcErrData.syncnr || i_lpchcErrData.syncne ||
        i_lpchcErrData.syncto || i_lpchcErrData.tctar || i_lpchcErrData.mctar)
    {
        o_resetLevel = RESET_OPB_LPCHC_SOFT;
    }
}

bool checkForLpcErrors()
{
    uint32_t opbm_buffer = 0;
    uint32_t lpchc_buffer = 0;
    OpbmErrReg_t opbm_err_union;
    opbm_err_union.data32 = 0x0;
    LpchcErrReg_t lpchc_err_union;
    lpchc_err_union.data32 = 0x0;
    ResetLevels l_opbmResetLevel = RESET_CLEAR;
    ResetLevels l_lpchcResetLevel = RESET_CLEAR;

    uint64_t l_pnorEnd = g_blData->blToHbData.lpcBAR + LPC::LPCHC_FW_SPACE +
        PNOR::LPC_TOP_OF_FLASH_OFFSET;

    uint64_t l_mmioAddr = l_pnorEnd - PNOR::TOC_OFFSET_FROM_TOP_OF_FLASH;
    l_mmioAddr = ALIGN_PAGE_DOWN(l_mmioAddr);

    // First do a dummy LPC access (if an LPC error condition exists,
    // an access can be necessary to get the error indicated in the
    // status register. This read will force the error condition to
    // properly be shown in the LPC error status reg
    Bootloader::handleMMIO(l_mmioAddr,
                           reinterpret_cast<uintptr_t>(&opbm_buffer) +
                           getHRMOR(),
                           Bootloader::WORDSIZE,
                           Bootloader::WORDSIZE,
                           Bootloader::READ);

    uint32_t l_opb_status_addr = OPBM_ACCUM_STATUS_REG;
    uint64_t src_addr = g_blData->blToHbData.lpcBAR + LPC::LPCHC_ERR_SPACE +
        l_opb_status_addr;
    Bootloader::handleMMIO(src_addr,
                           reinterpret_cast<uintptr_t>(&opbm_buffer) +
                           getHRMOR(),
                           Bootloader::WORDSIZE, Bootloader::WORDSIZE,
                           Bootloader::READ);

    uint32_t l_lpchc_addr = LPCHC_REG;
    src_addr = g_blData->blToHbData.lpcBAR + LPC::LPCHC_ERR_SPACE + l_lpchc_addr;
    Bootloader::handleMMIO(src_addr,
                           reinterpret_cast<uintptr_t>(&lpchc_buffer) +
                           getHRMOR(),
                           Bootloader::WORDSIZE, Bootloader::WORDSIZE,
                           Bootloader::READ);

    // Mask error bits
    opbm_err_union.data32 = (opbm_buffer & LPC::OPB_ERROR_MASK);
    lpchc_err_union.data32 = (lpchc_buffer & LPCHC_ERROR_MASK);

    if (opbm_err_union.data32 != 0)
    {
        computeOpbmErrSev(opbm_err_union, l_opbmResetLevel);

        if (l_opbmResetLevel != RESET_CLEAR)
        {
            bl_console::console_fail = true;
        }
    }

    if (lpchc_err_union.data32 != 0)
    {
        computeLpchcErrSev(lpchc_err_union, l_lpchcResetLevel);

        if (l_lpchcResetLevel != RESET_CLEAR)
        {
            bl_console::console_fail = true;
        }
    }

    return bl_console::console_fail;
}

static uint64_t convertSecToTicks(const uint64_t i_sec, const uint64_t i_nsec)
{
    // result = ((sec * 10^9 + nsec) * timebaseFreq Hz) / 10^9
    uint64_t timebaseFreq = 512000000ULL;
    uint64_t result = ((i_sec * 1000000000ULL) + i_nsec);
    result *= (timebaseFreq / 1000000);
    result /= 1000;
    return result;
}

// @TODO: RTC: 243863
// Evaluate timing on real hardware and tweak constants as needed
static void simpleDelay(const uint64_t i_sec, const uint64_t i_nsec)
{
    uint64_t delay = convertSecToTicks(i_sec, i_nsec);
    uint64_t expire = getTB() + delay;
    while (getTB() < expire)
    {
        // set low thread priority
        asm volatile("or 1,1,1");
    }

    // set high thread priority
    asm volatile("or 2,2,2");
}

void nanosleep(const uint64_t i_sec, const uint64_t i_nsec)
{
    uint64_t l_sec = i_sec + i_nsec / NS_PER_SEC;
    uint64_t l_nsec = i_nsec % NS_PER_SEC;

    simpleDelay(l_sec, l_nsec);
}

// TODO: RTC 209583 addon commit to handle error path
static bool readUartReg(const uint8_t i_addr, uint8_t &o_data)
{
    uint64_t src_addr = g_blData->blToHbData.lpcBAR + LPC::LPCHC_IO_SPACE + g_uartBase + i_addr;
    Bootloader::handleMMIO(src_addr,
                           reinterpret_cast<uintptr_t>(&o_data) + getHRMOR(),
                           sizeof(o_data), Bootloader::BYTESIZE,
                           Bootloader::READ);

    return checkForLpcErrors();
}

static bool writeUartReg(const uint8_t i_addr, const uint8_t i_data)
{
    uint64_t dest_addr = g_blData->blToHbData.lpcBAR + LPC::LPCHC_IO_SPACE + g_uartBase + i_addr;
    Bootloader::handleMMIO(reinterpret_cast<uintptr_t>(&i_data) + getHRMOR(),
                           dest_addr,
                           sizeof(i_data), Bootloader::BYTESIZE,
                           Bootloader::WRITE);

    return checkForLpcErrors();
}

void bl_console::putString(const char* i_s)
{
    while (*i_s)
    {
        putChar(*i_s);
        i_s++;
    }
}

// Everything in this function is taken from uart.C, but ideally will all be
// removed when SBE works
void bl_console::init()
{
    uint64_t divisor = (g_uartClock / 16) / g_uartBaud;
    uint8_t output = 0;

    if (!writeUartReg(LCR, 0x00) ||
        !writeUartReg(SCR, 'w') ||
        !readUartReg(SCR, output) ||
        !writeUartReg(IER, 0) ||
        !writeUartReg(LCR, LCR_DLAB) ||
        !writeUartReg(DLL, divisor & 0xff) ||
        !writeUartReg(DLM, divisor >> 8) ||
        !writeUartReg(LCR, LCR_DWL8 | LCR_NOP | LCR_STP1) ||
        !writeUartReg(MCR, MCR_RTS | MCR_DTR) ||
        !writeUartReg(FCR, FCR_ENF | FCR_CLFR | FCR_CLFT))
    {
        // Error already handled
    }
}

void displayNibble(const unsigned char i_nibble)
{
    // Optimization to take advantage of how ascii tables are constructed
    // Between 0-9 and A-F ascii characters, there are 7 other characters
    // 0xA is 10 in decimal, so being less than 10 would put you in the range
    // of 0-9, being greater than or equal to 10 would put you in the range of
    // A-F
    char hex_nibble = (i_nibble < 0xA) ? '0' + i_nibble : '7' + i_nibble;
    bl_console::putChar(hex_nibble);
}

void bl_console::displayHex(const unsigned char* i_start_addr, const size_t i_size)
{
    size_t count_size = 0;
    while (count_size < i_size)
    {
        if (count_size != 0)
        {
            // Code to format the console output to look more readable
            // Sample output:
            // XXXX XXXX XXXX XXXX
            // XXXX XXXX XXXX XXXX
            // 16 represents the end of line
            if (count_size % 16 == 0)
            {
                putString("\r\n");
            }
            // 4 represents the end of a block
            else if (count_size % 4 == 0)
            {
                putChar('\t');
            }
        }

        displayNibble((*i_start_addr & 0xF0) >> 4);
        displayNibble(*i_start_addr & 0x0F);
        count_size += sizeof(char);
        i_start_addr += sizeof(char);
    }
}

// Writes a character to console
void bl_console::putChar(const char i_c)
{
    // Wait for transmit FIFO to have space
    bool error = false;
    const auto blConfigData = reinterpret_cast<BootloaderConfigData_t *>(SBE_HB_COMM_ADDR);
    bool console_enable = (blConfigData->lpcConsoleEnable == 1);

    do
    {
        if (bl_console::console_fail || !console_enable)
        {
            break;
        }

        const uint64_t DELAY_NS = 100;
        const size_t DELAY_LOOPS = 100000000;

        uint8_t data = 0;
        size_t loops = 0;

        do
        {
            error = readUartReg(LSR, data);
            if (error)
            {
                break;
            }

            // Wait for idle or error status
            if (data == LSR_BAD || (data & LSR_THRE))
            {
                break;
            }
            nanosleep(0, DELAY_NS);

            loops++;
        } while (loops < DELAY_LOOPS);

        if (error)
        {
            break;
        }
        else if (data == LSR_BAD)
        {
            bl_console::console_fail = true;
            break;
        }
        else if (loops >= DELAY_LOOPS)
        {
            bl_console::console_fail = true;
            break;
        }

        // Write character to FIFO
        writeUartReg(THR, i_c);
    } while (0);
}
<commit_msg>Avoid all bootloader LPC console access when not enabled via SBE<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/bootloader/bl_console.C $                                 */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2019                             */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

/**
 *  @file bl_console.C
 *
 *  @brief Bootloader console functions to write data to lpc console
 */

#include <bootloader/bl_console.H>
#include <sys/task.h>
#include <sys/time.h>
#include <sys/syscall.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <lpc/lpc_const.H>
#include <uart.H>
#include <uartconfig.C>
#include <p10_sbe_hb_structures.H>
#include <util/align.H>
#include <arch/ppc.H>
#include <kernel/timemgr.H>

using namespace CONSOLE;
using namespace UARTREGS;

bool bl_console::console_fail = false;

void computeOpbmErrSev(OpbmErrReg_t i_opbmErrData,
                       ResetLevels &o_resetLevel)
{
    o_resetLevel = RESET_CLEAR;

    // First check the soft errors
    if (i_opbmErrData.rxits || i_opbmErrData.rxicmd || i_opbmErrData.rxiaa ||
        i_opbmErrData.rxopbe || i_opbmErrData.rxicmdb || i_opbmErrData.rxidatab)
    {
        o_resetLevel = RESET_OPB_LPCHC_SOFT;
    }

    // Now look for hard errors that will override soft errors reset level
    if (i_opbmErrData.rxopbt || i_opbmErrData.rxiaddr || i_opbmErrData.rxctgtel)
    {
        o_resetLevel = RESET_OPB_LPCHC_HARD;
    }
}

void computeLpchcErrSev(LpchcErrReg_t i_lpchcErrData,
                        ResetLevels &o_resetLevel)
{
    o_resetLevel = RESET_CLEAR;

    // First check the soft errors
    // All of these errors are set from bad LPC end points. Setting all to soft
    if (i_lpchcErrData.lreset || i_lpchcErrData.syncab ||
        i_lpchcErrData.syncnr || i_lpchcErrData.syncne ||
        i_lpchcErrData.syncto || i_lpchcErrData.tctar || i_lpchcErrData.mctar)
    {
        o_resetLevel = RESET_OPB_LPCHC_SOFT;
    }
}

bool checkForLpcErrors()
{
    uint32_t opbm_buffer = 0;
    uint32_t lpchc_buffer = 0;
    OpbmErrReg_t opbm_err_union;
    opbm_err_union.data32 = 0x0;
    LpchcErrReg_t lpchc_err_union;
    lpchc_err_union.data32 = 0x0;
    ResetLevels l_opbmResetLevel = RESET_CLEAR;
    ResetLevels l_lpchcResetLevel = RESET_CLEAR;

    uint64_t l_pnorEnd = g_blData->blToHbData.lpcBAR + LPC::LPCHC_FW_SPACE +
        PNOR::LPC_TOP_OF_FLASH_OFFSET;

    uint64_t l_mmioAddr = l_pnorEnd - PNOR::TOC_OFFSET_FROM_TOP_OF_FLASH;
    l_mmioAddr = ALIGN_PAGE_DOWN(l_mmioAddr);

    // First do a dummy LPC access (if an LPC error condition exists,
    // an access can be necessary to get the error indicated in the
    // status register. This read will force the error condition to
    // properly be shown in the LPC error status reg
    Bootloader::handleMMIO(l_mmioAddr,
                           reinterpret_cast<uintptr_t>(&opbm_buffer) +
                           getHRMOR(),
                           Bootloader::WORDSIZE,
                           Bootloader::WORDSIZE,
                           Bootloader::READ);

    uint32_t l_opb_status_addr = OPBM_ACCUM_STATUS_REG;
    uint64_t src_addr = g_blData->blToHbData.lpcBAR + LPC::LPCHC_ERR_SPACE +
        l_opb_status_addr;
    Bootloader::handleMMIO(src_addr,
                           reinterpret_cast<uintptr_t>(&opbm_buffer) +
                           getHRMOR(),
                           Bootloader::WORDSIZE, Bootloader::WORDSIZE,
                           Bootloader::READ);

    uint32_t l_lpchc_addr = LPCHC_REG;
    src_addr = g_blData->blToHbData.lpcBAR + LPC::LPCHC_ERR_SPACE + l_lpchc_addr;
    Bootloader::handleMMIO(src_addr,
                           reinterpret_cast<uintptr_t>(&lpchc_buffer) +
                           getHRMOR(),
                           Bootloader::WORDSIZE, Bootloader::WORDSIZE,
                           Bootloader::READ);

    // Mask error bits
    opbm_err_union.data32 = (opbm_buffer & LPC::OPB_ERROR_MASK);
    lpchc_err_union.data32 = (lpchc_buffer & LPCHC_ERROR_MASK);

    if (opbm_err_union.data32 != 0)
    {
        computeOpbmErrSev(opbm_err_union, l_opbmResetLevel);

        if (l_opbmResetLevel != RESET_CLEAR)
        {
            bl_console::console_fail = true;
        }
    }

    if (lpchc_err_union.data32 != 0)
    {
        computeLpchcErrSev(lpchc_err_union, l_lpchcResetLevel);

        if (l_lpchcResetLevel != RESET_CLEAR)
        {
            bl_console::console_fail = true;
        }
    }

    return bl_console::console_fail;
}

static uint64_t convertSecToTicks(const uint64_t i_sec, const uint64_t i_nsec)
{
    // result = ((sec * 10^9 + nsec) * timebaseFreq Hz) / 10^9
    uint64_t timebaseFreq = 512000000ULL;
    uint64_t result = ((i_sec * 1000000000ULL) + i_nsec);
    result *= (timebaseFreq / 1000000);
    result /= 1000;
    return result;
}

// @TODO: RTC: 243863
// Evaluate timing on real hardware and tweak constants as needed
static void simpleDelay(const uint64_t i_sec, const uint64_t i_nsec)
{
    uint64_t delay = convertSecToTicks(i_sec, i_nsec);
    uint64_t expire = getTB() + delay;
    while (getTB() < expire)
    {
        // set low thread priority
        asm volatile("or 1,1,1");
    }

    // set high thread priority
    asm volatile("or 2,2,2");
}

void nanosleep(const uint64_t i_sec, const uint64_t i_nsec)
{
    uint64_t l_sec = i_sec + i_nsec / NS_PER_SEC;
    uint64_t l_nsec = i_nsec % NS_PER_SEC;

    simpleDelay(l_sec, l_nsec);
}

// TODO: RTC 209583 addon commit to handle error path
static bool readUartReg(const uint8_t i_addr, uint8_t &o_data)
{
    uint64_t src_addr = g_blData->blToHbData.lpcBAR + LPC::LPCHC_IO_SPACE + g_uartBase + i_addr;
    Bootloader::handleMMIO(src_addr,
                           reinterpret_cast<uintptr_t>(&o_data) + getHRMOR(),
                           sizeof(o_data), Bootloader::BYTESIZE,
                           Bootloader::READ);

    return checkForLpcErrors();
}

static bool writeUartReg(const uint8_t i_addr, const uint8_t i_data)
{
    uint64_t dest_addr = g_blData->blToHbData.lpcBAR + LPC::LPCHC_IO_SPACE + g_uartBase + i_addr;
    Bootloader::handleMMIO(reinterpret_cast<uintptr_t>(&i_data) + getHRMOR(),
                           dest_addr,
                           sizeof(i_data), Bootloader::BYTESIZE,
                           Bootloader::WRITE);

    return checkForLpcErrors();
}

void bl_console::putString(const char* i_s)
{
    while (*i_s)
    {
        putChar(*i_s);
        i_s++;
    }
}

// Everything in this function is taken from uart.C, but ideally will all be
// removed when SBE works
void bl_console::init()
{
    uint64_t divisor = (g_uartClock / 16) / g_uartBaud;
    uint8_t output = 0;

    const auto blConfigData = reinterpret_cast<BootloaderConfigData_t *>(
        SBE_HB_COMM_ADDR);
    bool consoleEnabled = (blConfigData->lpcConsoleEnable == 1);

    if (!consoleEnabled ||
        !writeUartReg(LCR, 0x00) ||
        !writeUartReg(SCR, 'w') ||
        !readUartReg(SCR, output) ||
        !writeUartReg(IER, 0) ||
        !writeUartReg(LCR, LCR_DLAB) ||
        !writeUartReg(DLL, divisor & 0xff) ||
        !writeUartReg(DLM, divisor >> 8) ||
        !writeUartReg(LCR, LCR_DWL8 | LCR_NOP | LCR_STP1) ||
        !writeUartReg(MCR, MCR_RTS | MCR_DTR) ||
        !writeUartReg(FCR, FCR_ENF | FCR_CLFR | FCR_CLFT))
    {
        // Error already handled
    }
}

void displayNibble(const unsigned char i_nibble)
{
    // Optimization to take advantage of how ascii tables are constructed
    // Between 0-9 and A-F ascii characters, there are 7 other characters
    // 0xA is 10 in decimal, so being less than 10 would put you in the range
    // of 0-9, being greater than or equal to 10 would put you in the range of
    // A-F
    char hex_nibble = (i_nibble < 0xA) ? '0' + i_nibble : '7' + i_nibble;
    bl_console::putChar(hex_nibble);
}

void bl_console::displayHex(const unsigned char* i_start_addr, const size_t i_size)
{
    size_t count_size = 0;
    while (count_size < i_size)
    {
        if (count_size != 0)
        {
            // Code to format the console output to look more readable
            // Sample output:
            // XXXX XXXX XXXX XXXX
            // XXXX XXXX XXXX XXXX
            // 16 represents the end of line
            if (count_size % 16 == 0)
            {
                putString("\r\n");
            }
            // 4 represents the end of a block
            else if (count_size % 4 == 0)
            {
                putChar('\t');
            }
        }

        displayNibble((*i_start_addr & 0xF0) >> 4);
        displayNibble(*i_start_addr & 0x0F);
        count_size += sizeof(char);
        i_start_addr += sizeof(char);
    }
}

// Writes a character to console
void bl_console::putChar(const char i_c)
{
    // Wait for transmit FIFO to have space
    bool error = false;
    const auto blConfigData = reinterpret_cast<BootloaderConfigData_t *>(SBE_HB_COMM_ADDR);
    bool console_enable = (blConfigData->lpcConsoleEnable == 1);

    do
    {
        if (bl_console::console_fail || !console_enable)
        {
            break;
        }

        const uint64_t DELAY_NS = 100;
        const size_t DELAY_LOOPS = 100000000;

        uint8_t data = 0;
        size_t loops = 0;

        do
        {
            error = readUartReg(LSR, data);
            if (error)
            {
                break;
            }

            // Wait for idle or error status
            if (data == LSR_BAD || (data & LSR_THRE))
            {
                break;
            }
            nanosleep(0, DELAY_NS);

            loops++;
        } while (loops < DELAY_LOOPS);

        if (error)
        {
            break;
        }
        else if (data == LSR_BAD)
        {
            bl_console::console_fail = true;
            break;
        }
        else if (loops >= DELAY_LOOPS)
        {
            bl_console::console_fail = true;
            break;
        }

        // Write character to FIFO
        writeUartReg(THR, i_c);
    } while (0);
}
<|endoftext|>


<commit_before>// Training Library
#include "../lib/std_lib_facilities.h"

int main()
{
    // Prompt user to enter name
    cout << "Please enter your first name: \n";

    // Setup variable, then read input into it
    string first_name;
    cin >> first_name;

    /*
     * cin refers to the standard input stream,
     * defined in the standard library
     *
     * The >> operator ("get from") specifies where cin 'goes'
     */

    // Do something with the input
    cout << "Hello, " << first_name << "!\n;";

    return 0;
}

/*
 * Drill 2
 *
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 * /

#include "../lib/std_lib_facilities.h"

int main()
{
    cout << "Please enter your first name and age\n";
    string first_name;
    int age;

    cin >> first_name;
    cin >> age;

    cout << "Hello, " << first_name << " (age "<< age << ")\n";

    return 0;
}
 */<commit_msg>Updated lib reference<commit_after>// Training Library
#include "../../lib/std_lib_facilities.h"

int main()
{
    // Prompt user to enter name
    cout << "Please enter your first name: \n";

    // Setup variable, then read input into it
    string first_name;
    cin >> first_name;

    /*
     * cin refers to the standard input stream,
     * defined in the standard library
     *
     * The >> operator ("get from") specifies where cin 'goes'
     */

    // Do something with the input
    cout << "Hello, " << first_name << "!\n;";

    return 0;
}

/*
 * Drill 2
 *
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 * /

#include "../lib/std_lib_facilities.h"

int main()
{
    cout << "Please enter your first name and age\n";
    string first_name;
    int age;

    cin >> first_name;
    cin >> age;

    cout << "Hello, " << first_name << " (age "<< age << ")\n";

    return 0;
}
 */<|endoftext|>


<commit_before>/*
 * Copyright 2022 Google LLC

 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at

 *      http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/* A detector that uses ptrace to identify shell injection vulnerabilities. */

/* C standard library */
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>

/* POSIX */
#include <sys/stat.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <unistd.h>

/* Linux */
#include <sys/ptrace.h>
#include <syscall.h>

#include <fstream>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <vector>

#define DEBUG_LOGS 0

#if DEBUG_LOGS
#define debug_log(...)            \
  do {                            \
    fprintf(stderr, __VA_ARGS__); \
    fflush(stdout);               \
    fputc('\n', stderr);          \
  } while (0)
#else
#define debug_log(...)
#endif

#define fatal_log(...)            \
  do {                            \
    fprintf(stderr, __VA_ARGS__); \
    fputc('\n', stderr);          \
    exit(EXIT_FAILURE);           \
  } while (0)

// The magic string that we'll use to detect full control over the command
// executed.
const std::string kTripWire = "/tmp/tripwire";
// Shell injection bug confirmed with /tmp/tripwire.
const std::string kInjectionError = "Shell injection";
// Shell corruption bug detected based on syntax error.
const std::string kCorruptionError = "Shell corruption";

// The PID of the root process we're fuzzing.
pid_t g_root_pid;
// Assuming the longest pathname is "/bin/bash".
constexpr int kShellPathnameLength = 20;

// Syntax error messages of each shell.
const std::map<std::string, std::set<std::string>> kShellSyntaxErrors = {
    {"bash",
     {
         ": command not found",  // General
         ": syntax error",       // Unfinished " or ' or ` or if, leading | or ;
         ": missing `]'",        // Unfinished [
         ": event not found",    // ! leads large numbers
         ": No such file or directory",  // Leading < or /
     }},
    {"csh",
     {
         ": Command not found.",         // General
         ": Missing }.",                 // Unfinished {
         "Too many ('s.",                // Unfinished (
         "Invalid null command.",        // Leading | or < or >
         "Missing name for redirect.",   // Single < or >
         ": No match.",                  // Leading ? or [ or *
         "Modifier failed.",             // Leading ^
         "No previous left hand side.",  // A ^
         ": No such job.",               // Leading %
         ": No current job.",            // A %
         ": Undefined variable.",        // Containing $
         ": Event not found.",           // ! leads large numbers
         // TODO: Make this more specific.
         "Unmatched",  // Unfinished " or ' or `, leading ;
     }},
    {"dash",
     {
         "not found",     // General
         "Syntax error",  // Unfinished " or ' or ` or if, leading | or ; or &
         "missing ]",     // Unfinished [
         "No such file",  // Leading <
     }},
    {"zsh",
     {
         ": command not found",                // General
         ": syntax error",                     // Unfinished " or ' or `
         ": ']' expected",                     // Unfinished [
         ": no such file or directory",        // Leading < or /
         ": parse error near",                 // Leading |, or &
         ": no such user or named directory",  // Leading ~
     }},
};

// Shells used by Processes.
std::map<pid_t, std::string> g_shell_pids;

struct Tracee {
  pid_t pid;
  bool syscall_enter = true;

  Tracee(pid_t pid) : pid(pid) {}
};

pid_t run_child(char **argv) {
  // Run the program under test with its args as a child process
  pid_t pid = fork();
  switch (pid) {
    case -1:
      fatal_log("Fork failed: %s", strerror(errno));
    case 0:
      raise(SIGSTOP);
      execvp(argv[0], argv);
      fatal_log("execvp: %s", strerror(errno));
  }
  return pid;
}

std::vector<std::byte> read_memory(pid_t pid, unsigned long long address,
                                   size_t size) {
  std::vector<std::byte> memory;

  for (size_t i = 0; i < size; i += sizeof(long)) {
    long word = ptrace(PTRACE_PEEKTEXT, pid, address + i, 0);
    if (word == -1) {
      return memory;
    }

    std::byte *word_bytes = reinterpret_cast<std::byte *>(&word);
    memory.insert(memory.end(), word_bytes, word_bytes + sizeof(long));
  }

  return memory;
}

// Construct a string with the memory specified in a register.
std::string read_string(pid_t pid, unsigned long reg, unsigned long length) {
  auto memory = read_memory(pid, reg, length);
  if (!memory.size()) {
    return "";
  }

  std::string content(reinterpret_cast<char *>(memory.data()),
                      std::min(memory.size(), length));
  return content;
}

void report_bug(std::string bug_type) {
  // Report the bug found based on the bug code.
  std::cerr << "===BUG DETECTED: " << bug_type.c_str() << "===\n";
  // Rely on sanitizers/libFuzzer to produce a stacktrace by sending SIGABRT
  // to the root process.
  // Note: this may not be reliable or consistent if shell injection happens
  // in an async way.
  kill(g_root_pid, SIGABRT);
  _exit(0);
}

void inspect_for_injection(pid_t pid, const user_regs_struct &regs) {
  // Inspect a PID's registers for the sign of shell injection.
  std::string path = read_string(pid, regs.rdi, kTripWire.length());
  if (!path.length()) {
    return;
  }
  debug_log("inspecting");
  if (path == kTripWire) {
    report_bug(kInjectionError);
  }
}

std::string get_pathname(pid_t pid, const user_regs_struct &regs) {
  // Parse the pathname from the memory specified in the RDI register.
  std::string pathname = read_string(pid, regs.rdi, kShellPathnameLength);
  debug_log("Pathname is %s (len %lu)\n", pathname.c_str(), pathname.length());
  return pathname;
}

std::string match_shell(std::string binary_pathname);

// Identify the exact shell behind sh
std::string identify_sh(std::string binary_name) {
  char shell_pathname[kShellPathnameLength];
  if (readlink(binary_name.c_str(), shell_pathname, kShellPathnameLength) ==
      -1) {
    std::cerr << "Cannot query which shell is behind sh: readlink failed\n";
    std::cerr << "Assuming the shell is dash\n";
    return "dash";
  }
  debug_log("sh links to %s\n", shell_pathname);
  std::string shell_pathname_str(shell_pathname);

  return match_shell(shell_pathname_str);
}

std::string match_shell(std::string binary_pathname) {
  // Identify the name of the shell used in the pathname.
  if (!binary_pathname.length()) {
    return "";
  }
  for (const auto &item : kShellSyntaxErrors) {
    std::string known_shell = item.first;
    std::string binary_name = binary_pathname.substr(
        binary_pathname.find_last_of("/") + 1, known_shell.length());
    debug_log("Binary is %s (%lu)\n", binary_name.c_str(),
              binary_name.length());
    if (!binary_name.compare(0, 2, "sh")) {
      debug_log("Matched sh: Needs to identify which specific shell it is.\n");
      return identify_sh(binary_pathname);
    }
    if (binary_name == known_shell) {
      debug_log("Matched %s\n", binary_name.c_str());
      return known_shell;
    }
  }
  return "";
}

std::string get_shell(pid_t pid, const user_regs_struct &regs) {
  // Get shell name used in a process.
  std::string binary_pathname = get_pathname(pid, regs);
  return match_shell(binary_pathname);
}

void match_error_pattern(std::string buffer, std::string shell) {
  auto error_patterns = kShellSyntaxErrors.at(shell);
  for (const auto &pattern : error_patterns) {
    debug_log("Pattern : %s\n", pattern.c_str());
    debug_log("Found at: %lu\n", buffer.find(pattern));
    if (buffer.find(pattern) != std::string::npos) {
      std::cerr << "--- Found a sign of shell corruption ---\n"
                << buffer.c_str()
                << "\n----------------------------------------\n";
      report_bug(kCorruptionError);
    }
  }
}

void inspect_for_corruption(pid_t pid, const user_regs_struct &regs) {
  // Inspect a PID's registers for shell corruption.
  std::string buffer = read_string(pid, regs.rsi, regs.rdx);
  debug_log("Write buffer: %s\n", buffer.c_str());
  match_error_pattern(buffer, g_shell_pids[pid]);
}

void trace(std::map<pid_t, Tracee> pids) {
  while (!pids.empty()) {
    std::vector<pid_t> new_pids;

    auto it = pids.begin();

    while (it != pids.end()) {
      auto pid = it->first;
      auto &tracee = it->second;
      int status = 0;

      int result = waitpid(pid, &status, __WALL | WNOHANG);
      if (result == -1) {
        it = pids.erase(it);
        continue;
      }

      if (result == 0) {
        // Nothing to report yet.
        ++it;
        continue;
      }

      debug_log("finished waiting %d", pid);

      if (WIFEXITED(status) || WIFSIGNALED(status)) {
        debug_log("%d exited", pid);
        it = pids.erase(it);
        // Remove pid from the watchlist when it exits
        g_shell_pids.erase(pid);
        continue;
      }

      // ptrace sets 0x80 for syscalls (with PTRACE_O_TRACESYSGOOD set).
      bool is_syscall =
          WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80);
      int sig = 0;
      if (!is_syscall) {
        // Handle generic signal.
        siginfo_t siginfo;
        if (ptrace(PTRACE_GETSIGINFO, pid, nullptr, &siginfo) == -1) {
          debug_log("ptrace(PTRACE_GETSIGINFO, %d): %s", pid, strerror(errno));
          continue;
        }
        sig = siginfo.si_signo;
        debug_log("forwarding signal %d to %d", sig, pid);
      }

      if (WIFSTOPPED(status) &&
          (status >> 8 == (SIGTRAP | (PTRACE_EVENT_CLONE << 8)) ||
           status >> 8 == (SIGTRAP | (PTRACE_EVENT_FORK << 8)) ||
           status >> 8 == (SIGTRAP | (PTRACE_EVENT_VFORK << 8)))) {
        long new_pid;
        if (ptrace(PTRACE_GETEVENTMSG, pid, 0, &new_pid) == -1) {
          debug_log("ptrace(PTRACE_GETEVENTMSG, %d): %s", pid, strerror(errno));
          continue;
        }
        debug_log("forked %ld", new_pid);
        new_pids.push_back(new_pid);
      }

      if (is_syscall) {
        user_regs_struct regs;
        if (ptrace(PTRACE_GETREGS, pid, 0, &regs) == -1) {
          debug_log("ptrace(PTRACE_GETREGS, %d): %s", pid, strerror(errno));
          continue;
        }

        if (tracee.syscall_enter) {
          if (regs.orig_rax == __NR_execve) {
            inspect_for_injection(pid, regs);
            std::string shell = get_shell(pid, regs);
            if (shell != "") {
              debug_log("Shell parsed: %s", shell.c_str());
              g_shell_pids.insert(std::make_pair(pid, shell));
            }
          }

          if (regs.orig_rax == __NR_write &&
              g_shell_pids.find(pid) != g_shell_pids.end()) {
            debug_log("Inspecting the `write` buffer of shell process %d.",
                      pid);
            inspect_for_corruption(pid, regs);
          }
        }

        // TODO: Check for commands with invalid syntax passed to /bin/sh and
        // other shells.
        // TODO: It's possible the process we're fuzzing can communicate with
        // another process to execute code. Our check wouldn't catch this
        // currently.
        tracee.syscall_enter = !tracee.syscall_enter;
      }

      debug_log("tracing %d %d", pid, sig);
      if (ptrace(PTRACE_SYSCALL, pid, nullptr, sig) == -1) {
        debug_log("ptrace(PTRACE_SYSCALL, %d): %s", pid, strerror(errno));
        continue;
      }

      ++it;
    }

    for (const auto &pid : new_pids) {
      pids.emplace(pid, Tracee(pid));
    }
  }
}

int main(int argc, char **argv) {
  if (argc <= 1) {
    fatal_log("Expecting at least one arguments, received %d", argc - 1);
  }

  // Create an executable tripwire file, as programs may check for existence
  // before actually calling exec.
  std::ofstream tripwire(kTripWire);
  tripwire.close();
  chmod(kTripWire.c_str(), 0755);

  pid_t pid = run_child(argv + 1);

  long options = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK |
                 PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE;

  if (ptrace(PTRACE_SEIZE, pid, nullptr, options) == -1) {
    fatal_log("ptrace(PTRACE_SEIZE): %s", strerror(errno));
  }

  if (waitpid(pid, nullptr, __WALL) == -1) {
    fatal_log("waitpid: %s", strerror(errno));
  }

  if (ptrace(PTRACE_SYSCALL, pid, 0, 0) == -1) {
    fatal_log("ptrace(PTRACE_SYSCALL): %s", strerror(errno));
  }

  g_root_pid = pid;
  std::map<pid_t, Tracee> pids;
  pids.emplace(pid, Tracee(pid));
  trace(pids);
}
<commit_msg>execSan: Return same exit status as the child. (#7924)<commit_after>/*
 * Copyright 2022 Google LLC

 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at

 *      http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/* A detector that uses ptrace to identify shell injection vulnerabilities. */

/* C standard library */
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>

/* POSIX */
#include <sys/stat.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <unistd.h>

/* Linux */
#include <sys/ptrace.h>
#include <syscall.h>

#include <fstream>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <vector>

#define DEBUG_LOGS 0

#if DEBUG_LOGS
#define debug_log(...)            \
  do {                            \
    fprintf(stderr, __VA_ARGS__); \
    fflush(stdout);               \
    fputc('\n', stderr);          \
  } while (0)
#else
#define debug_log(...)
#endif

#define fatal_log(...)            \
  do {                            \
    fprintf(stderr, __VA_ARGS__); \
    fputc('\n', stderr);          \
    exit(EXIT_FAILURE);           \
  } while (0)

// The magic string that we'll use to detect full control over the command
// executed.
const std::string kTripWire = "/tmp/tripwire";
// Shell injection bug confirmed with /tmp/tripwire.
const std::string kInjectionError = "Shell injection";
// Shell corruption bug detected based on syntax error.
const std::string kCorruptionError = "Shell corruption";

// The PID of the root process we're fuzzing.
pid_t g_root_pid;
// Assuming the longest pathname is "/bin/bash".
constexpr int kShellPathnameLength = 20;

// Syntax error messages of each shell.
const std::map<std::string, std::set<std::string>> kShellSyntaxErrors = {
    {"bash",
     {
         ": command not found",  // General
         ": syntax error",       // Unfinished " or ' or ` or if, leading | or ;
         ": missing `]'",        // Unfinished [
         ": event not found",    // ! leads large numbers
         ": No such file or directory",  // Leading < or /
     }},
    {"csh",
     {
         ": Command not found.",         // General
         ": Missing }.",                 // Unfinished {
         "Too many ('s.",                // Unfinished (
         "Invalid null command.",        // Leading | or < or >
         "Missing name for redirect.",   // Single < or >
         ": No match.",                  // Leading ? or [ or *
         "Modifier failed.",             // Leading ^
         "No previous left hand side.",  // A ^
         ": No such job.",               // Leading %
         ": No current job.",            // A %
         ": Undefined variable.",        // Containing $
         ": Event not found.",           // ! leads large numbers
         // TODO: Make this more specific.
         "Unmatched",  // Unfinished " or ' or `, leading ;
     }},
    {"dash",
     {
         "not found",     // General
         "Syntax error",  // Unfinished " or ' or ` or if, leading | or ; or &
         "missing ]",     // Unfinished [
         "No such file",  // Leading <
     }},
    {"zsh",
     {
         ": command not found",                // General
         ": syntax error",                     // Unfinished " or ' or `
         ": ']' expected",                     // Unfinished [
         ": no such file or directory",        // Leading < or /
         ": parse error near",                 // Leading |, or &
         ": no such user or named directory",  // Leading ~
     }},
};

// Shells used by Processes.
std::map<pid_t, std::string> g_shell_pids;

struct Tracee {
  pid_t pid;
  bool syscall_enter = true;

  Tracee(pid_t pid) : pid(pid) {}
};

pid_t run_child(char **argv) {
  // Run the program under test with its args as a child process
  pid_t pid = fork();
  switch (pid) {
    case -1:
      fatal_log("Fork failed: %s", strerror(errno));
    case 0:
      raise(SIGSTOP);
      execvp(argv[0], argv);
      fatal_log("execvp: %s", strerror(errno));
  }
  return pid;
}

std::vector<std::byte> read_memory(pid_t pid, unsigned long long address,
                                   size_t size) {
  std::vector<std::byte> memory;

  for (size_t i = 0; i < size; i += sizeof(long)) {
    long word = ptrace(PTRACE_PEEKTEXT, pid, address + i, 0);
    if (word == -1) {
      return memory;
    }

    std::byte *word_bytes = reinterpret_cast<std::byte *>(&word);
    memory.insert(memory.end(), word_bytes, word_bytes + sizeof(long));
  }

  return memory;
}

// Construct a string with the memory specified in a register.
std::string read_string(pid_t pid, unsigned long reg, unsigned long length) {
  auto memory = read_memory(pid, reg, length);
  if (!memory.size()) {
    return "";
  }

  std::string content(reinterpret_cast<char *>(memory.data()),
                      std::min(memory.size(), length));
  return content;
}

void report_bug(std::string bug_type) {
  // Report the bug found based on the bug code.
  std::cerr << "===BUG DETECTED: " << bug_type.c_str() << "===\n";
  // Rely on sanitizers/libFuzzer to produce a stacktrace by sending SIGABRT
  // to the root process.
  // Note: this may not be reliable or consistent if shell injection happens
  // in an async way.
  kill(g_root_pid, SIGABRT);
  _exit(0);
}

void inspect_for_injection(pid_t pid, const user_regs_struct &regs) {
  // Inspect a PID's registers for the sign of shell injection.
  std::string path = read_string(pid, regs.rdi, kTripWire.length());
  if (!path.length()) {
    return;
  }
  debug_log("inspecting");
  if (path == kTripWire) {
    report_bug(kInjectionError);
  }
}

std::string get_pathname(pid_t pid, const user_regs_struct &regs) {
  // Parse the pathname from the memory specified in the RDI register.
  std::string pathname = read_string(pid, regs.rdi, kShellPathnameLength);
  debug_log("Pathname is %s (len %lu)\n", pathname.c_str(), pathname.length());
  return pathname;
}

std::string match_shell(std::string binary_pathname);

// Identify the exact shell behind sh
std::string identify_sh(std::string binary_name) {
  char shell_pathname[kShellPathnameLength];
  if (readlink(binary_name.c_str(), shell_pathname, kShellPathnameLength) ==
      -1) {
    std::cerr << "Cannot query which shell is behind sh: readlink failed\n";
    std::cerr << "Assuming the shell is dash\n";
    return "dash";
  }
  debug_log("sh links to %s\n", shell_pathname);
  std::string shell_pathname_str(shell_pathname);

  return match_shell(shell_pathname_str);
}

std::string match_shell(std::string binary_pathname) {
  // Identify the name of the shell used in the pathname.
  if (!binary_pathname.length()) {
    return "";
  }
  for (const auto &item : kShellSyntaxErrors) {
    std::string known_shell = item.first;
    std::string binary_name = binary_pathname.substr(
        binary_pathname.find_last_of("/") + 1, known_shell.length());
    debug_log("Binary is %s (%lu)\n", binary_name.c_str(),
              binary_name.length());
    if (!binary_name.compare(0, 2, "sh")) {
      debug_log("Matched sh: Needs to identify which specific shell it is.\n");
      return identify_sh(binary_pathname);
    }
    if (binary_name == known_shell) {
      debug_log("Matched %s\n", binary_name.c_str());
      return known_shell;
    }
  }
  return "";
}

std::string get_shell(pid_t pid, const user_regs_struct &regs) {
  // Get shell name used in a process.
  std::string binary_pathname = get_pathname(pid, regs);
  return match_shell(binary_pathname);
}

void match_error_pattern(std::string buffer, std::string shell) {
  auto error_patterns = kShellSyntaxErrors.at(shell);
  for (const auto &pattern : error_patterns) {
    debug_log("Pattern : %s\n", pattern.c_str());
    debug_log("Found at: %lu\n", buffer.find(pattern));
    if (buffer.find(pattern) != std::string::npos) {
      std::cerr << "--- Found a sign of shell corruption ---\n"
                << buffer.c_str()
                << "\n----------------------------------------\n";
      report_bug(kCorruptionError);
    }
  }
}

void inspect_for_corruption(pid_t pid, const user_regs_struct &regs) {
  // Inspect a PID's registers for shell corruption.
  std::string buffer = read_string(pid, regs.rsi, regs.rdx);
  debug_log("Write buffer: %s\n", buffer.c_str());
  match_error_pattern(buffer, g_shell_pids[pid]);
}

int trace(std::map<pid_t, Tracee> pids) {
  unsigned long exit_status = 0;
  while (!pids.empty()) {
    std::vector<pid_t> new_pids;

    auto it = pids.begin();

    while (it != pids.end()) {
      auto pid = it->first;
      auto &tracee = it->second;
      int status = 0;

      int result = waitpid(pid, &status, __WALL | WNOHANG);
      if (result == -1) {
        it = pids.erase(it);
        continue;
      }

      if (result == 0) {
        // Nothing to report yet.
        ++it;
        continue;
      }

      debug_log("finished waiting %d", pid);

      if (WIFEXITED(status) || WIFSIGNALED(status)) {
        debug_log("%d exited", pid);
        it = pids.erase(it);
        // Remove pid from the watchlist when it exits
        g_shell_pids.erase(pid);
        continue;
      }

      // ptrace sets 0x80 for syscalls (with PTRACE_O_TRACESYSGOOD set).
      bool is_syscall =
          WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80);
      int sig = 0;
      if (!is_syscall) {
        // Handle generic signal.
        siginfo_t siginfo;
        if (ptrace(PTRACE_GETSIGINFO, pid, nullptr, &siginfo) == -1) {
          debug_log("ptrace(PTRACE_GETSIGINFO, %d): %s", pid, strerror(errno));
          continue;
        }
        sig = siginfo.si_signo;
        debug_log("forwarding signal %d to %d", sig, pid);
      }

      if ((status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXIT << 8)))) {
        debug_log("%d exiting", pid);
        if (pid == g_root_pid) {
          if (ptrace(PTRACE_GETEVENTMSG, pid, 0, &exit_status) == -1) {
            debug_log("ptrace(PTRACE_GETEVENTMSG, %d): %s", pid, strerror(errno));
          }
          debug_log("got exit status from root process: %lu", exit_status);
        }

        if (ptrace(PTRACE_DETACH, pid, 0, 0) == -1) {
          debug_log("ptrace(PTRACE_DETACH, %d): %s", pid, strerror(errno));
        }
        continue;
      }

      if (WIFSTOPPED(status) &&
          (status >> 8 == (SIGTRAP | (PTRACE_EVENT_CLONE << 8)) ||
           status >> 8 == (SIGTRAP | (PTRACE_EVENT_FORK << 8)) ||
           status >> 8 == (SIGTRAP | (PTRACE_EVENT_VFORK << 8)))) {
        long new_pid;
        if (ptrace(PTRACE_GETEVENTMSG, pid, 0, &new_pid) == -1) {
          debug_log("ptrace(PTRACE_GETEVENTMSG, %d): %s", pid, strerror(errno));
          continue;
        }
        debug_log("forked %ld", new_pid);
        new_pids.push_back(new_pid);
      }

      if (is_syscall) {
        user_regs_struct regs;
        if (ptrace(PTRACE_GETREGS, pid, 0, &regs) == -1) {
          debug_log("ptrace(PTRACE_GETREGS, %d): %s", pid, strerror(errno));
          continue;
        }

        if (tracee.syscall_enter) {
          if (regs.orig_rax == __NR_execve) {
            inspect_for_injection(pid, regs);
            std::string shell = get_shell(pid, regs);
            if (shell != "") {
              debug_log("Shell parsed: %s", shell.c_str());
              g_shell_pids.insert(std::make_pair(pid, shell));
            }
          }

          if (regs.orig_rax == __NR_write &&
              g_shell_pids.find(pid) != g_shell_pids.end()) {
            debug_log("Inspecting the `write` buffer of shell process %d.",
                      pid);
            inspect_for_corruption(pid, regs);
          }
        }

        // TODO: Check for commands with invalid syntax passed to /bin/sh and
        // other shells.
        // TODO: It's possible the process we're fuzzing can communicate with
        // another process to execute code. Our check wouldn't catch this
        // currently.
        tracee.syscall_enter = !tracee.syscall_enter;
      }

      debug_log("tracing %d %d", pid, sig);
      if (ptrace(PTRACE_SYSCALL, pid, nullptr, sig) == -1) {
        debug_log("ptrace(PTRACE_SYSCALL, %d): %s", pid, strerror(errno));
        continue;
      }

      ++it;
    }

    for (const auto &pid : new_pids) {
      pids.emplace(pid, Tracee(pid));
    }
  }
  return static_cast<int>(exit_status >> 8);
}

int main(int argc, char **argv) {
  if (argc <= 1) {
    fatal_log("Expecting at least one arguments, received %d", argc - 1);
  }

  // Create an executable tripwire file, as programs may check for existence
  // before actually calling exec.
  std::ofstream tripwire(kTripWire);
  tripwire.close();
  chmod(kTripWire.c_str(), 0755);

  pid_t pid = run_child(argv + 1);

  long options = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK |
                 PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE |
                 PTRACE_O_TRACEEXIT;

  if (ptrace(PTRACE_SEIZE, pid, nullptr, options) == -1) {
    fatal_log("ptrace(PTRACE_SEIZE): %s", strerror(errno));
  }

  if (waitpid(pid, nullptr, __WALL) == -1) {
    fatal_log("waitpid: %s", strerror(errno));
  }

  if (ptrace(PTRACE_SYSCALL, pid, 0, 0) == -1) {
    fatal_log("ptrace(PTRACE_SYSCALL): %s", strerror(errno));
  }

  g_root_pid = pid;
  std::map<pid_t, Tracee> pids;
  pids.emplace(pid, Tracee(pid));
  return trace(pids);
}
<|endoftext|>


<commit_before>#include <vector>
#include <string>
#include <limits>
#include <memory>
#include <iostream>

#include <arpa/inet.h>

#include "caf/all.hpp"
#include "caf/io/all.hpp"

CAF_PUSH_WARNINGS
#include "message.pb.h"
CAF_POP_WARNINGS

using namespace std;
using namespace caf;
using namespace caf::io;

void print_on_exit(const actor& hdl, const std::string& name) {
  hdl->attach_functor([=](abstract_actor* ptr, uint32_t reason) {
    aout(ptr) << name << " exited with reason " << reason << endl;
  });
}

/*
  **************************
  Client's behavior -- sends a warning after kickoff and quits
  after the warning has been acknowledged by the server
  **************************
*/
behavior sendWarning(event_based_actor* self) {
  auto count = make_shared<size_t>(0);
  return {
    on(atom("kickoff"), arg_match) >> [=](const actor& pong) {
//	  aout(self) << "client: send {" << to_string(atom("warning")::value) << ", " <<  1 << "}" << endl;
      self->send(pong, atom("warning"), 1);
      self->become (
        on(atom("ack"), arg_match) >> [=](int value) -> void {
          self->quit();
          return;
//          return make_message(atom("ping"), value + 1);
        }
      );
    }
  };
}

/*
  **************************
  Server's behavior -- gets the client's warning from the protobuf broker
  and responds with an "ack" message
  **************************
*/
behavior ackMessage() {
  return {
    on(atom("warning"), arg_match) >> [](int value) {
      return make_message(atom("ack"), value);
    }
  };
}

/*
  **************************
  Protobuf broker implementation
  Works as a communication handling middleman for the server/client actors above
  Deserizalizes/serializes messages
  **************************
*/
void protobuf_io(broker* self, connection_handle hdl, const actor& buddy) {
  // monitor buddy to quit broker if buddy is done
  self->monitor(buddy);
  auto write = [=](const org::libcppa::WarnOrAck& p) {
    string buf = p.SerializeAsString();
    int32_t s = htonl(static_cast<int32_t>(buf.size()));
    self->write(hdl, sizeof(int32_t), &s);
    self->write(hdl, buf.size(), buf.data());
    self->flush(hdl);
  };
  // message handler
  message_handler default_bhvr = {
    [=](const connection_closed_msg&) {
      aout(self) << "connection closed" << endl;
      self->send_exit(buddy, exit_reason::remote_link_unreachable);
      self->quit(exit_reason::remote_link_unreachable);
    },
    on(atom("warning"), arg_match) >> [=](int i) {
      aout(self) << "Send: warning " << i << endl;
      org::libcppa::WarnOrAck p;
      p.mutable_warn()->set_id(i);
      write(p);
    },
    on(atom("ack"), arg_match) >> [=](int i) {
      aout(self) << "Send: ack " << i << endl;
      org::libcppa::WarnOrAck p;
      p.mutable_ack()->set_id(i);
      write(p);
    },
    [=](const down_msg& dm) {
      if (dm.source == buddy) {
        aout(self) << "our buddy is down" << endl;
        self->quit(dm.reason);
      }
    },
    others >> [=] {
      cout << "unexpected: " << to_string(self->current_message()) << endl;
    }
  };
  auto await_protobuf_data = message_handler {
    [=](const new_data_msg& msg) {
      org::libcppa::WarnOrAck p;
      p.ParseFromArray(msg.buf.data(), static_cast<int>(msg.buf.size()));
      if (p.has_warn()) {
        aout(self) << "Received: warning " << p.warn().id() << endl;
        self->send(buddy, atom("warning"), p.warn().id());
      }
      else if (p.has_ack()) {
        aout(self) << "Received: ack " << p.ack().id() << endl;
        self->send(buddy, atom("ack"), p.ack().id());
      }
      else {
        self->quit(exit_reason::user_shutdown);
        cerr << "neither Warning nor ACK!" << endl;
      }
      // receive next length prefix
      self->configure_read(hdl, receive_policy::exactly(sizeof(int32_t)));
      self->unbecome();
    }
  }.or_else(default_bhvr);
  auto await_length_prefix = message_handler {
    [=](const new_data_msg& msg) {
      int32_t num_bytes;
      memcpy(&num_bytes, msg.buf.data(), sizeof(int32_t));
      num_bytes = htonl(num_bytes);
      if (num_bytes < 0 || num_bytes > (1024 * 1024)) {
        aout(self) << "someone is trying something nasty" << endl;
        self->quit(exit_reason::user_shutdown);
        return;
      }
      // receive protobuf data
      auto nb = static_cast<size_t>(num_bytes);
      self->configure_read(hdl, receive_policy::exactly(nb));
      self->become(keep_behavior, await_protobuf_data);
    }
  }.or_else(default_bhvr);
  // initial setup
  self->configure_read(hdl, receive_policy::exactly(sizeof(int32_t)));
  self->become(await_length_prefix);
}

/*
 **************************
 Helper method for the server to handle connections;
 Starts the server broker with the received message handle if a client connects
 **************************
*/
behavior server(broker* self, actor buddy) {
  aout(self) << "server is running" << endl;
  return {
    [=](const new_connection_msg& msg) {
      aout(self) << "server accepted new connection" << endl;
      auto io_actor = self->fork(protobuf_io, msg.handle, buddy);
      print_on_exit(io_actor, "protobuf_io");
      // only accept 1 connection in our example
      self->quit();
    },
    others >> [=] {
      cout << "unexpected: " << to_string(self->current_message()) << endl;
    }
  };
}

optional<uint16_t> as_u16(const std::string& str) {
  return static_cast<uint16_t>(stoul(str));
}

int main(int argc, char** argv) {
// Parsing arguments
  message_builder{argv + 1, argv + argc}.apply({
    on("-s", as_u16) >> [&](uint16_t port) {
    // We are a server
      cout << "run in server mode" << endl;
      // spawn the actor that acks messages
      auto serv_actor = spawn(ackMessage);
      // spawn an IO server to handle communication and work with the broker later (uses the behavior server helper)
      auto sever_actor = spawn_io_server(server, port, serv_actor);
      print_on_exit(sever_actor, "server");
      print_on_exit(serv_actor, "exit ack");
    },
    on("-c", val<string>, as_u16) >> [&](const string& host, uint16_t port) {
      // spawn the actor that sends warnings
      auto client_actor = spawn(sendWarning);
      // spawn a broker to serialize and recognize messages
      auto io_actor = spawn_io_client(protobuf_io, host, port, client_actor);
      print_on_exit(io_actor, "protobuf_io");
      print_on_exit(client_actor, "exit warning");
      // Start the warning message actor by using a kickoff message event sent directly from main
      send_as(io_actor, client_actor, atom("kickoff"), io_actor);
    },
    others >> [] {
      cerr << "use with eihter '-s PORT' as server or "
          "'-c HOST PORT' as client"
           << endl;
    }
  });
  await_all_actors_done();
  shutdown();
}
<commit_msg>cleanup<commit_after>#include <vector>
#include <string>
#include <limits>
#include <memory>
#include <iostream>

#include <arpa/inet.h>

#include "caf/all.hpp"
#include "caf/io/all.hpp"

CAF_PUSH_WARNINGS
#include "message.pb.h"
CAF_POP_WARNINGS

using namespace std;
using namespace caf;
using namespace caf::io;

void print_on_exit(const actor& hdl, const std::string& name) {
  hdl->attach_functor([=](abstract_actor* ptr, uint32_t reason) {
    aout(ptr) << name << " exited with reason " << reason << endl;
  });
}

/*
  **************************
  Client's behavior -- sends a warning after kickoff and quits
  after the warning has been acknowledged by the server
  **************************
*/
behavior sendWarning(event_based_actor* self) {
  auto count = make_shared<size_t>(0);
  return {
    on(atom("kickoff"), arg_match) >> [=](const actor& pong) {
      self->send(pong, atom("warning"), 1);
      self->become (
        on(atom("ack"), arg_match) >> [=](int value) -> void {
          self->quit();
          return;
        }
      );
    }
  };
}

/*
  **************************
  Server's behavior -- gets the client's warning from the protobuf broker
  and responds with an "ack" message
  **************************
*/
behavior ackMessage() {
  return {
    on(atom("warning"), arg_match) >> [](int value) {
      return make_message(atom("ack"), value);
    }
  };
}

/*
  **************************
  Protobuf broker implementation
  Works as a communication handling middleman for the server/client actors above
  Deserizalizes/serializes messages
  **************************
*/
void protobuf_io(broker* self, connection_handle hdl, const actor& buddy) {
  // monitor buddy to quit broker if buddy is done
  self->monitor(buddy);
  auto write = [=](const org::libcppa::WarnOrAck& p) {
    string buf = p.SerializeAsString();
    int32_t s = htonl(static_cast<int32_t>(buf.size()));
    self->write(hdl, sizeof(int32_t), &s);
    self->write(hdl, buf.size(), buf.data());
    self->flush(hdl);
  };
  // message handler
  message_handler default_bhvr = {
    [=](const connection_closed_msg&) {
      aout(self) << "connection closed" << endl;
      self->send_exit(buddy, exit_reason::remote_link_unreachable);
      self->quit(exit_reason::remote_link_unreachable);
    },
    on(atom("warning"), arg_match) >> [=](int i) {
      aout(self) << "Send: warning " << i << endl;
      org::libcppa::WarnOrAck p;
      p.mutable_warn()->set_id(i);
      write(p);
    },
    on(atom("ack"), arg_match) >> [=](int i) {
      aout(self) << "Send: ack " << i << endl;
      org::libcppa::WarnOrAck p;
      p.mutable_ack()->set_id(i);
      write(p);
    },
    [=](const down_msg& dm) {
      if (dm.source == buddy) {
        aout(self) << "our buddy is down" << endl;
        self->quit(dm.reason);
      }
    },
    others >> [=] {
      cout << "unexpected: " << to_string(self->current_message()) << endl;
    }
  };
  auto await_protobuf_data = message_handler {
    [=](const new_data_msg& msg) {
      org::libcppa::WarnOrAck p;
      p.ParseFromArray(msg.buf.data(), static_cast<int>(msg.buf.size()));
      if (p.has_warn()) {
        aout(self) << "Received: warning " << p.warn().id() << endl;
        self->send(buddy, atom("warning"), p.warn().id());
      }
      else if (p.has_ack()) {
        aout(self) << "Received: ack " << p.ack().id() << endl;
        self->send(buddy, atom("ack"), p.ack().id());
      }
      else {
        self->quit(exit_reason::user_shutdown);
        cerr << "neither Warning nor ACK!" << endl;
      }
      // receive next length prefix
      self->configure_read(hdl, receive_policy::exactly(sizeof(int32_t)));
      self->unbecome();
    }
  }.or_else(default_bhvr);
  auto await_length_prefix = message_handler {
    [=](const new_data_msg& msg) {
      int32_t num_bytes;
      memcpy(&num_bytes, msg.buf.data(), sizeof(int32_t));
      num_bytes = htonl(num_bytes);
      if (num_bytes < 0 || num_bytes > (1024 * 1024)) {
        aout(self) << "someone is trying something nasty" << endl;
        self->quit(exit_reason::user_shutdown);
        return;
      }
      // receive protobuf data
      auto nb = static_cast<size_t>(num_bytes);
      self->configure_read(hdl, receive_policy::exactly(nb));
      self->become(keep_behavior, await_protobuf_data);
    }
  }.or_else(default_bhvr);
  // initial setup
  self->configure_read(hdl, receive_policy::exactly(sizeof(int32_t)));
  self->become(await_length_prefix);
}

/*
 **************************
 Helper method for the server to handle connections;
 Starts the server broker with the received message handle if a client connects
 **************************
*/
behavior server(broker* self, actor buddy) {
  aout(self) << "server is running" << endl;
  return {
    [=](const new_connection_msg& msg) {
      aout(self) << "server accepted new connection" << endl;
      auto io_actor = self->fork(protobuf_io, msg.handle, buddy);
      print_on_exit(io_actor, "protobuf_io");
      // only accept 1 connection in our example
      self->quit();
    },
    others >> [=] {
      cout << "unexpected: " << to_string(self->current_message()) << endl;
    }
  };
}

optional<uint16_t> as_u16(const std::string& str) {
  return static_cast<uint16_t>(stoul(str));
}

int main(int argc, char** argv) {
// Parsing arguments
  message_builder{argv + 1, argv + argc}.apply({
    on("-s", as_u16) >> [&](uint16_t port) {
    // We are a server
      cout << "run in server mode" << endl;
      // spawn the actor that acks messages
      auto serv_actor = spawn(ackMessage);
      // spawn an IO server to handle communication and work with the broker later (uses the behavior server helper)
      auto sever_actor = spawn_io_server(server, port, serv_actor);
      print_on_exit(sever_actor, "server");
      print_on_exit(serv_actor, "exit ack");
    },
    on("-c", val<string>, as_u16) >> [&](const string& host, uint16_t port) {
      // spawn the actor that sends warnings
      auto client_actor = spawn(sendWarning);
      // spawn a broker to serialize and recognize messages
      auto io_actor = spawn_io_client(protobuf_io, host, port, client_actor);
      print_on_exit(io_actor, "protobuf_io");
      print_on_exit(client_actor, "exit warning");
      // Start the warning message actor by using a kickoff message event sent directly from main
      send_as(io_actor, client_actor, atom("kickoff"), io_actor);
    },
    others >> [] {
      cerr << "use with eihter '-s PORT' as server or "
          "'-c HOST PORT' as client"
           << endl;
    }
  });
  await_all_actors_done();
  shutdown();
}
<|endoftext|>