mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-05 23:41:44 +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
|
||||
auto stream = fopen(fn.c_str(), "rb");
|
||||
#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);
|
||||
buf_.resize((size_t)ftell(stream));
|
||||
rewind(stream);
|
||||
@@ -84,6 +91,13 @@ struct PagedFileImpl final : FileReader::Impl {
|
||||
#else
|
||||
fp_ = fopen(fn.c_str(), "rb");
|
||||
#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);
|
||||
file_size_ = (size_t)ftell(fp_);
|
||||
rewind(fp_);
|
||||
|
||||
Reference in New Issue
Block a user