iguana
iguana copied to clipboard
heap-buffer-overflow in from_yaml using yaml_skip_till
Summary
The YAML reader’s yaml_skip_till<':'> loop omits an end check before dereferencing, so a malformed mapping key lets from_yaml read past the heap buffer while searching for ':'. ASan reports a heap-buffer-overflow and the process terminates.
Versions
Versions tested and affected:
- 1.09
- latest git master
$ git rev-parse HEAD
4b6fb4dfe96485d727f5e5cb5bff7b8e2a62783c
Build and test platform
Ubuntu 24.04.3
Test case
$ clang++ -std=c++20 -g -O1 -fsanitize=address -I. \
-o tool/pocs/poc_driver tool/pocs/poc_driver.cpp
$ ASAN_OPTIONS=detect_leaks=0:abort_on_error=0 \
./tool/pocs/poc_driver yaml_skip_till.yaml
PoC input: yaml_skip_till.yaml
#include <cstdint>
#include <fstream>
#include <iostream>
#include <map>
#include <optional>
#include <sstream>
#include <string>
#include <vector>
#include "iguana/json_reader.hpp"
#include "iguana/xml_reader.hpp"
#include "iguana/yaml_reader.hpp"
namespace poc {
struct NetworkRoute {
std::string address;
std::vector<int> ports;
bool secure;
};
YLT_REFL(NetworkRoute, address, ports, secure);
struct Credential {
std::string user;
std::optional<std::string> secret;
std::vector<uint8_t> salt;
};
YLT_REFL(Credential, user, secret, salt);
struct Telemetry {
std::vector<double> metrics;
std::vector<float> readings;
std::optional<std::string> last_error;
};
YLT_REFL(Telemetry, metrics, readings, last_error);
struct ServiceConfig {
std::string name;
std::map<int, Credential> fallback_credentials;
std::vector<NetworkRoute> routes;
Telemetry telemetry;
};
YLT_REFL(ServiceConfig, name, fallback_credentials, routes, telemetry);
struct SerConfig {
std::string name;
};
YLT_REFL(SerConfig, name);
std::string read_file(const std::string &path) {
std::ifstream ifs(path, std::ios::binary);
if (!ifs) {
throw std::runtime_error("failed to open " + path);
}
std::ostringstream oss;
oss << ifs.rdbuf();
return oss.str();
}
} // namespace poc
int main(int argc, char **argv) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0]
<< " <json|xml|yaml-map|yaml-str> <path>\n";
return 1;
}
const std::string mode = argv[1];
const std::string path = argv[2];
const std::string payload = poc::read_file(path);
try {
if (mode == "json") {
poc::ServiceConfig cfg;
iguana::from_json(cfg, payload);
} else if (mode == "xml") {
poc::SerConfig cfg;
iguana::from_xml(cfg, payload);
} else if (mode == "yaml-map") {
poc::ServiceConfig cfg;
iguana::from_yaml(cfg, payload);
} else if (mode == "yaml-str") {
poc::ServiceConfig cfg;
iguana::from_yaml(cfg, payload);
} else {
std::cerr << "unknown mode: " << mode << "\n";
return 1;
}
} catch (const std::exception &ex) {
std::cerr << "parser threw: " << ex.what() << "\n";
return 0;
}
std::cout << "parsed successfully\n";
return 0;
}
Latest git master
Confirmed by ASAN:
=================================================================
==526342==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50300000006f
#0 iguana::yaml_skip_till<(char)58>(...) ./iguana/yaml_util.hpp:85
#1 iguana::from_yaml<poc::ServiceConfig>(...) ./iguana/yaml_reader.hpp:554