mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 06:39:13 +00:00
ifcparse: check the result of fopen before using the FILE*
FullBufferImpl and PagedFileImpl both open the file and then use the handle
without ever testing it:
auto stream = _wfopen(fn_wide, L"rb"); // null when the file is missing
fseek(stream, 0, SEEK_END); // null goes straight to the CRT
buf_.resize((size_t)ftell(stream));
Opening a path that does not exist therefore hands a null FILE* to the CRT. On
MSVC that does not return an error: the runtime terminates the process
immediately (fastfail, exit code 0xC0000409). No exception is thrown, no stack
unwinding starts, so a caller cannot defend with try/catch — the host
application simply dies. On glibc it is undefined behaviour as well.
This is reachable through the ordinary entry point, because guess_file_type()
answers FT_IFCSPF for a path that does not exist (its own comment calls this
"just weird, but for consistency with earlier behaviour"), so a missing path
flows into the reader rather than being reported.
The fix is to leave the reader empty when the open fails. Both implementations
then behave like a zero-length file: size() is 0 and get() throws out_of_range
for any position, so the parse fails and IfcFile::good() reports it, which is
what a caller can actually handle. PagedFileImpl's destructor already tested
fp_ for null, so the possibility was known — only the constructor did not check.
Verified by reading a non-existent path through IfcParse::IfcFile: the
constructor returns and good() reports the failure, where before the process
died with 0xC0000409 and no output.
This commit is contained in:
@@ -46,6 +46,13 @@ struct FullBufferImpl final : FileReader::Impl {
|
|||||||
#else
|
#else
|
||||||
auto stream = fopen(fn.c_str(), "rb");
|
auto stream = fopen(fn.c_str(), "rb");
|
||||||
#endif
|
#endif
|
||||||
|
if (stream == nullptr) {
|
||||||
|
// Missing or unreadable file. Leave the buffer empty so the
|
||||||
|
// caller sees a zero-length input and reports a read error;
|
||||||
|
// handing a null FILE* to the CRT below terminates the whole
|
||||||
|
// process instead of failing the parse.
|
||||||
|
return;
|
||||||
|
}
|
||||||
fseek(stream, 0, SEEK_END);
|
fseek(stream, 0, SEEK_END);
|
||||||
buf_.resize((size_t)ftell(stream));
|
buf_.resize((size_t)ftell(stream));
|
||||||
rewind(stream);
|
rewind(stream);
|
||||||
@@ -84,6 +91,13 @@ struct PagedFileImpl final : FileReader::Impl {
|
|||||||
#else
|
#else
|
||||||
fp_ = fopen(fn.c_str(), "rb");
|
fp_ = fopen(fn.c_str(), "rb");
|
||||||
#endif
|
#endif
|
||||||
|
if (fp_ == nullptr) {
|
||||||
|
// As above: behave like an empty file rather than passing a
|
||||||
|
// null FILE* to fseek. get() then throws out_of_range for any
|
||||||
|
// position and fetchPage_() is never reached.
|
||||||
|
file_size_ = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
fseek(fp_, 0, SEEK_END);
|
fseek(fp_, 0, SEEK_END);
|
||||||
file_size_ = (size_t)ftell(fp_);
|
file_size_ = (size_t)ftell(fp_);
|
||||||
rewind(fp_);
|
rewind(fp_);
|
||||||
|
|||||||
Reference in New Issue
Block a user