Skip to content

Commit fd078b6

Browse files
authored
Merge pull request #10712 from Fermions-ASI/fix/10082
odb: validate 3dblox input file paths before creating DB objects (#10082)
2 parents 373fb09 + ed75610 commit fd078b6

10 files changed

Lines changed: 84 additions & 13 deletions

File tree

src/odb/src/3dblox/baseParser.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <vector>
1212

1313
#include "objects.h"
14+
#include "utl/CFileUtils.h"
1415
#include "utl/Logger.h"
1516
#include "yaml-cpp/yaml.h"
1617
namespace odb {
@@ -178,6 +179,11 @@ void BaseParser::logError(const std::string& message)
178179
utl::ODB, 521, "Parser Error in {}: {}", current_file_path_, message);
179180
}
180181

182+
std::ifstream BaseParser::openInputFile()
183+
{
184+
return utl::OpenInputStream(current_file_path_, logger_);
185+
}
186+
181187
std::string BaseParser::trim(const std::string& str)
182188
{
183189
size_t start = str.find_first_not_of(" \t\n\r");

src/odb/src/3dblox/baseParser.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#pragma once
55

6+
#include <fstream>
67
#include <string>
78
#include <vector>
89

@@ -37,6 +38,13 @@ class BaseParser
3738
std::string resolvePath(const std::string& path);
3839
void resolvePaths(const std::string& path, std::vector<std::string>& paths);
3940

41+
// Opens an input file for reading after validating that the path exists and
42+
// refers to a regular, readable file. On any failure this reports a clean
43+
// logger error (which is non-returning) BEFORE the caller creates any DB
44+
// objects, preventing downstream crashes on invalid/nonexistent paths.
45+
// current_file_path_ must be set to the file path before calling.
46+
std::ifstream openInputFile();
47+
4048
// Utility methods
4149
void logError(const std::string& message);
4250
std::string trim(const std::string& str);

src/odb/src/3dblox/bmapParser.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <vector>
1111

1212
#include "objects.h"
13+
#include "utl/CFileUtils.h"
1314
#include "utl/Logger.h"
1415
namespace odb {
1516

@@ -20,11 +21,7 @@ BmapParser::BmapParser(utl::Logger* logger) : logger_(logger)
2021
BumpMapData BmapParser::parseFile(const std::string& filename)
2122
{
2223
current_file_path_ = filename;
23-
std::ifstream file(filename);
24-
if (!file.is_open()) {
25-
logger_->error(
26-
utl::ODB, 535, "Bump Map Parser Error: Cannot open file: {}", filename);
27-
}
24+
std::ifstream file = utl::OpenInputStream(filename, logger_);
2825

2926
std::stringstream buffer;
3027
buffer << file.rdbuf();

src/odb/src/3dblox/dbvParser.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ DbvParser::DbvParser(utl::Logger* logger) : BaseParser(logger)
2525
DbvData DbvParser::parseFile(const std::string& filename)
2626
{
2727
current_file_path_ = filename;
28-
std::ifstream file(filename);
29-
if (!file.is_open()) {
30-
logError("Cannot open file");
31-
}
28+
std::ifstream file = openInputFile();
3229

3330
std::stringstream buffer;
3431
buffer << file.rdbuf();

src/odb/src/3dblox/dbxParser.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,7 @@ DbxParser::DbxParser(utl::Logger* logger) : BaseParser(logger)
2424
DbxData DbxParser::parseFile(const std::string& filename)
2525
{
2626
current_file_path_ = filename;
27-
std::ifstream file(filename);
28-
if (!file.is_open()) {
29-
logError("Cannot open file");
30-
}
27+
std::ifstream file = openInputFile();
3128

3229
std::stringstream buffer;
3330
buffer << file.rdbuf();

src/odb/test/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ COMPULSORY_TESTS = [
220220
PASSFAIL_TESTS = [
221221
# pass-fail
222222
"read_3dbv_fail",
223+
"read_3dbx_fail",
223224
"read_def_virtual_invalid",
224225
"test_block",
225226
"test_bterm",

src/odb/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ or_integration_tests(
7474
PASSFAIL_TESTS
7575
cpp_tests
7676
read_3dbv_fail
77+
read_3dbx_fail
7778
read_def_virtual_invalid
7879
test_block
7980
test_bterm

src/odb/test/read_3dbx_fail.tcl

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Regression for issue #10082: read_3dbx must report a clean error (not crash)
2+
# on an invalid / nonexistent path, instead of proceeding to create DB objects
3+
# with an uninitialized state and segfaulting.
4+
source "helpers.tcl"
5+
6+
# Test 1: nonexistent file passed to read_3dbx.
7+
# Caught by the read_3dbx Tcl wrapper before reaching the C++ reader.
8+
set status [catch { read_3dbx "data/this_file_does_not_exist.3dbx" } msg]
9+
if { $status != 1 } {
10+
puts "fail: read_3dbx accepted a nonexistent path"
11+
exit 1
12+
}
13+
if { ![regexp "ORD-0072" $msg] } {
14+
puts "fail: unexpected error for nonexistent path: $msg"
15+
exit 1
16+
}
17+
18+
# Test 2: invalid path that exists but is not a regular file (a directory).
19+
# This bypasses the Tcl wrapper's "file exists" check and exercises the C++
20+
# 3dblox reader's file-validation guard directly. Before the fix the reader's
21+
# std::ifstream::is_open() succeeded on a directory (on Linux), so parsing
22+
# proceeded on empty content and could create DB objects with uninitialized
23+
# state -> segfault. Call the C++ command directly to reach that path.
24+
set dir_path [make_result_file "read_3dbx_fail_dir.3dbx"]
25+
file delete -force $dir_path
26+
file mkdir $dir_path
27+
set status [catch { ord::read_3dbx_cmd $dir_path } msg]
28+
file delete -force $dir_path
29+
if { $status != 1 } {
30+
puts "fail: read_3dbx_cmd accepted a directory path"
31+
exit 1
32+
}
33+
if { ![regexp "UTL-0015" $msg] } {
34+
puts "fail: unexpected error for directory path: $msg"
35+
exit 1
36+
}
37+
38+
puts "pass"

src/utl/include/utl/CFileUtils.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <cstdint>
77
#include <cstdio>
8+
#include <fstream>
89
#include <span>
910
#include <string>
1011

@@ -35,4 +36,11 @@ std::string GetContents(FILE* file, Logger* logger);
3536
// of "data" has been written successfully.
3637
void WriteAll(FILE* file, std::span<const uint8_t> data, Logger* logger);
3738

39+
// Opens "filename" as a text input stream after validating that it refers to an
40+
// existing regular file. std::ifstream::is_open() alone is insufficient: on
41+
// Linux it succeeds for directories (and other non-regular files), so the
42+
// caller would silently read empty content. On failure logger->error is used to
43+
// throw.
44+
std::ifstream OpenInputStream(const std::string& filename, Logger* logger);
45+
3846
} // namespace utl

src/utl/src/CFileUtils.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include <cstdint>
77
#include <cstdio>
88
#include <cstring>
9+
#include <filesystem>
10+
#include <fstream>
911
#include <span>
1012
#include <string>
1113

@@ -76,4 +78,20 @@ void WriteAll(FILE* file, std::span<const uint8_t> data, Logger* logger)
7678
}
7779
}
7880

81+
std::ifstream OpenInputStream(const std::string& filename, Logger* logger)
82+
{
83+
// is_regular_file() returns false for a nonexistent path, a directory, or any
84+
// other non-regular file, so a single check rejects all the cases where a
85+
// plain ifstream would otherwise open (or appear to open) and read nothing.
86+
std::error_code ec;
87+
if (!std::filesystem::is_regular_file(filename, ec)) {
88+
logger->error(UTL, 15, "{} is not a readable file", filename);
89+
}
90+
std::ifstream stream(filename);
91+
if (!stream.is_open()) {
92+
logger->error(UTL, 16, "failed to open file {}", filename);
93+
}
94+
return stream;
95+
}
96+
7997
} // namespace utl

0 commit comments

Comments
 (0)