mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-28 19:31:22 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5023acce1 |
@@ -307,14 +307,17 @@ bool PrusaFileParser::check_3mf_from_prusa(const std::string filename)
|
||||
|
||||
mz_zip_archive_file_stat stat;
|
||||
if (!mz_zip_reader_file_stat(&archive, model_file_index, &stat)) goto EXIT;
|
||||
// expat sizes its buffer with an int, so a larger entry cannot be parsed in one piece.
|
||||
if (stat.m_uncomp_size > static_cast<mz_uint64>(std::numeric_limits<int>::max())) goto EXIT;
|
||||
|
||||
void *parser_buffer = XML_GetBuffer(m_parser, (int) stat.m_uncomp_size);
|
||||
const int xml_size = static_cast<int>(stat.m_uncomp_size);
|
||||
void *parser_buffer = XML_GetBuffer(m_parser, xml_size);
|
||||
if (parser_buffer == nullptr) goto EXIT;
|
||||
|
||||
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t) stat.m_uncomp_size, 0);
|
||||
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, static_cast<size_t>(xml_size), 0);
|
||||
if (res == 0) goto EXIT;
|
||||
|
||||
XML_ParseBuffer(m_parser, (int) stat.m_uncomp_size, 1);
|
||||
XML_ParseBuffer(m_parser, xml_size, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1346,19 +1349,26 @@ ModelVolumeType type_from_string(const std::string &s)
|
||||
XML_SetUserData(m_xml_parser, (void*)this);
|
||||
XML_SetElementHandler(m_xml_parser, _3MF_Importer::_handle_start_config_xml_element, _3MF_Importer::_handle_end_config_xml_element);
|
||||
|
||||
void* parser_buffer = XML_GetBuffer(m_xml_parser, (int)stat.m_uncomp_size);
|
||||
// expat sizes its buffer with an int, so a larger entry cannot be parsed in one piece.
|
||||
if (stat.m_uncomp_size > static_cast<mz_uint64>(std::numeric_limits<int>::max())) {
|
||||
add_error("Found invalid size");
|
||||
return false;
|
||||
}
|
||||
const int xml_size = static_cast<int>(stat.m_uncomp_size);
|
||||
|
||||
void* parser_buffer = XML_GetBuffer(m_xml_parser, xml_size);
|
||||
if (parser_buffer == nullptr) {
|
||||
add_error("Unable to create buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t)stat.m_uncomp_size, 0);
|
||||
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, static_cast<size_t>(xml_size), 0);
|
||||
if (res == 0) {
|
||||
add_error("Error while reading config data to buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!XML_ParseBuffer(m_xml_parser, (int)stat.m_uncomp_size, 1)) {
|
||||
if (!XML_ParseBuffer(m_xml_parser, xml_size, 1)) {
|
||||
char error_buf[1024];
|
||||
::sprintf(error_buf, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), (int)XML_GetCurrentLineNumber(m_xml_parser));
|
||||
add_error(error_buf);
|
||||
|
||||
@@ -2508,19 +2508,26 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
XML_SetEntityDeclHandler(m_xml_parser, nullptr);
|
||||
XML_SetExternalEntityRefHandler(m_xml_parser, nullptr);
|
||||
|
||||
void* parser_buffer = XML_GetBuffer(m_xml_parser, (int)stat.m_uncomp_size);
|
||||
// expat sizes its buffer with an int, so a larger entry cannot be parsed in one piece.
|
||||
if (stat.m_uncomp_size > static_cast<mz_uint64>(std::numeric_limits<int>::max())) {
|
||||
add_error("Found invalid size");
|
||||
return false;
|
||||
}
|
||||
const int xml_size = static_cast<int>(stat.m_uncomp_size);
|
||||
|
||||
void* parser_buffer = XML_GetBuffer(m_xml_parser, xml_size);
|
||||
if (parser_buffer == nullptr) {
|
||||
add_error("Unable to create buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t)stat.m_uncomp_size, 0);
|
||||
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, static_cast<size_t>(xml_size), 0);
|
||||
if (res == 0) {
|
||||
add_error("Error while reading config data to buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!XML_ParseBuffer(m_xml_parser, (int)stat.m_uncomp_size, 1)) {
|
||||
if (!XML_ParseBuffer(m_xml_parser, xml_size, 1)) {
|
||||
char error_buf[1024];
|
||||
::snprintf(error_buf, 1024, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), (int)XML_GetCurrentLineNumber(m_xml_parser));
|
||||
add_error(error_buf);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
@@ -1461,3 +1462,79 @@ SCENARIO("bbs_3mf_is_published detects only genuinely published 3MFs", "[3mf]")
|
||||
}
|
||||
}
|
||||
|
||||
// Writes a single-entry zip whose central directory carries a zip64 record declaring an
|
||||
// uncompressed size beyond what the 32-bit expat buffer API can take, while the deflated
|
||||
// payload inflates to only ~64 KiB. Built by hand because miniz never writes a size that
|
||||
// disagrees with the data.
|
||||
static void write_zip_with_oversized_entry(const std::string& path, const std::string& entry)
|
||||
{
|
||||
const std::string xml = "<?xml version=\"1.0\"?><!--" + std::string(65536, 'A') + "--><a/>";
|
||||
size_t comp_len = 0;
|
||||
void* comp = tdefl_compress_mem_to_heap(xml.data(), xml.size(), &comp_len, TDEFL_DEFAULT_MAX_PROBES);
|
||||
REQUIRE(comp != nullptr);
|
||||
const std::string deflated(static_cast<const char*>(comp), comp_len);
|
||||
mz_free(comp);
|
||||
const uint32_t crc = static_cast<uint32_t>(mz_crc32(MZ_CRC32_INIT, reinterpret_cast<const unsigned char*>(xml.data()), xml.size()));
|
||||
const uint64_t claimed_size = (uint64_t(1) << 32) + 16;
|
||||
|
||||
std::string out;
|
||||
auto put = [&out](uint64_t v, int bytes) {
|
||||
for (int i = 0; i < bytes; ++i)
|
||||
out.push_back(static_cast<char>((v >> (8 * i)) & 0xFF));
|
||||
};
|
||||
// local file header, with the true sizes
|
||||
put(0x04034b50, 4); put(45, 2); put(0, 2); put(8, 2); put(0, 2); put(0, 2);
|
||||
put(crc, 4); put(deflated.size(), 4); put(xml.size(), 4); put(entry.size(), 2); put(0, 2);
|
||||
out += entry + deflated;
|
||||
// central directory header, sizes deferred to the zip64 extra field
|
||||
const size_t cd_offset = out.size();
|
||||
put(0x02014b50, 4); put(45, 2); put(45, 2); put(0, 2); put(8, 2); put(0, 2); put(0, 2);
|
||||
put(crc, 4); put(0xFFFFFFFF, 4); put(0xFFFFFFFF, 4); put(entry.size(), 2); put(20, 2);
|
||||
put(0, 2); put(0, 2); put(0, 2); put(0, 4); put(0, 4);
|
||||
out += entry;
|
||||
put(0x0001, 2); put(16, 2); put(claimed_size, 8); put(deflated.size(), 8);
|
||||
const size_t cd_size = out.size() - cd_offset;
|
||||
// end of central directory
|
||||
put(0x06054b50, 4); put(0, 2); put(0, 2); put(1, 2); put(1, 2);
|
||||
put(cd_size, 4); put(cd_offset, 4); put(0, 2);
|
||||
|
||||
boost::nowide::ofstream f(path, std::ios::binary);
|
||||
REQUIRE(f.good());
|
||||
f.write(out.data(), static_cast<std::streamsize>(out.size()));
|
||||
REQUIRE(f.good());
|
||||
}
|
||||
|
||||
TEST_CASE("3MF XML entries declaring more than an int can hold fail to load", "[3mf]") {
|
||||
ScopedTemporaryFile temp(".3mf");
|
||||
const std::string path = temp.string();
|
||||
|
||||
SECTION("BBS importer") {
|
||||
write_zip_with_oversized_entry(path, "_rels/.rels");
|
||||
Model model;
|
||||
DynamicPrintConfig config;
|
||||
ConfigSubstitutionContext ctxt{ForwardCompatibilitySubstitutionRule::Enable};
|
||||
PlateDataPtrs plates;
|
||||
std::vector<Preset*> project_presets;
|
||||
bool is_bbl_3mf = false, is_orca_3mf = false;
|
||||
Semver file_version;
|
||||
bool loaded = true;
|
||||
REQUIRE_NOTHROW(loaded = load_bbs_3mf(path.c_str(), &config, &ctxt, &model, &plates, &project_presets, &is_bbl_3mf,
|
||||
&is_orca_3mf, &file_version, nullptr, LoadStrategy::LoadModel | LoadStrategy::LoadConfig));
|
||||
CHECK_FALSE(loaded);
|
||||
release_PlateData_list(plates);
|
||||
}
|
||||
SECTION("PrusaSlicer importer") {
|
||||
write_zip_with_oversized_entry(path, "Metadata/Slic3r_PE_model.config");
|
||||
Model model;
|
||||
DynamicPrintConfig config;
|
||||
ConfigSubstitutionContext ctxt{ForwardCompatibilitySubstitutionRule::Disable};
|
||||
bool loaded = true;
|
||||
REQUIRE_NOTHROW(loaded = load_3mf(path.c_str(), config, ctxt, &model, false));
|
||||
CHECK_FALSE(loaded);
|
||||
}
|
||||
SECTION("PrusaSlicer fingerprint probe") {
|
||||
write_zip_with_oversized_entry(path, "3D/3dmodel.model");
|
||||
PrusaFileParser parser;
|
||||
CHECK_FALSE(parser.check_3mf_from_prusa(path));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user