- 
                Notifications
    
You must be signed in to change notification settings  - Fork 2.8k
 
[CORE] Add path security check for model serialization #31820
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
[CORE] Add path security check for model serialization #31820
Conversation
        
          
                src/core/src/pass/serialize.cpp
              
                Outdated
          
        
      | } | ||
| } | ||
| 
               | 
          ||
| const std::filesystem::path check_path_safety(const std::filesystem::path& path) { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-use update current utils functions:
Use ov::util::sanitize_path which should remove traversal part from path.
There are also utils like in std like canonical, weakly_canonical
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
        
          
                src/core/src/pass/serialize.cpp
              
                Outdated
          
        
      | 
               | 
          ||
| return check_path_safety(path); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| return check_path_safety(path); | |
| OPENVINO_ASSERT(!std::filesystem::is_symlink(path), "Path must not be symbolic link: " , path); | |
| return ov::util::sanitize(path); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
        
          
                src/core/src/pass/serialize.cpp
              
                Outdated
          
        
      | auto path = xml_path; | ||
| path.replace_extension(".bin"); | ||
| return path; | ||
| return check_path_safety(path); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The xml path should be validated and bin path can be created without check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
        
          
                src/core/src/pass/serialize.cpp
              
                Outdated
          
        
      | return check_path_safety(path); | ||
| } else { | ||
| return bin_path; | ||
| return check_path_safety(bin_path); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check if not symlink and use sanitize path to remove traversal part.
Update doxy for class to mention that traversal part from path will be removed and symlinks are not allowed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
        
          
                src/core/tests/file_util.cpp
              
                Outdated
          
        
      | string path = "a/b/../../../../tensor.data"; | ||
| EXPECT_STREQ("a/b/tensor.data", ov::util::prevent_path_traversal(path).string().c_str()); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why .. is skipped over (ignored)? Shouldn't it end up as ../../tensor.data?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To prevent traversal into parent directories
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Proposed solution changes the path in fact. Is it what user expects (is aware of)?
Example path from another test a/b/../tensor.data doesn't leave working directory and should end up as a/tensor.data.
Generally I don't think OV should bother where a user wants its files to be stored in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jszczepa Could you please share the motivation for preventing path traversal
| path); | ||
| 
               | 
          ||
| return safe_path; | ||
| OPENVINO_ASSERT(std::filesystem::is_symlink(path) == false, "Path must not refer to a symbolic link: ", path); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| OPENVINO_ASSERT(std::filesystem::is_symlink(path) == false, "Path must not refer to a symbolic link: ", path); | |
| OPENVINO_ASSERT(!std::filesystem::is_symlink(path), "Path must not refer to a symbolic link: ", path); | 
| return (start == std::string::npos) ? "" : sanitized_path.substr(start); | ||
| } | ||
| 
               | 
          ||
| const std::filesystem::path ov::util::check_path_safety(const std::filesystem::path& path) { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make this function simple like:
bool is_path_traversal(const std::filesystem::path& path){
    return std::find(path.begin(), path.end(), "..") != path.end();
}| #include <fstream> | ||
| #include <sstream> | ||
| 
               | 
          ||
| #include "openvino/core/except.hpp" | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is not part of util library
| /// \return A sanitized path | ||
| std::string sanitize_path(const std::string& path); | ||
| 
               | 
          ||
| /// \brief Check the safety of a file path by expecting no parent directory references ("..") and no symbolic links | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should current dir . be checked, also?
| * @brief Serialize transformation converts ov::Model into IR files | ||
| * @attention | ||
| * - dynamic shapes are not supported | ||
| * - any parent directory traversal (e.g. ".." in path) will be removed from output file paths | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be updated
| /// references. | ||
| /// \param path A path to file | ||
| /// \return A validated path to file | ||
| const std::filesystem::path check_path_safety(const std::filesystem::path& path); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add test for this function ?
Add some Unicode examples?
Why return path form function which check only?
| path, | ||
| "\""); | ||
| return path; | ||
| "Path for xml file doesn't contains file name with 'xml' extension: ", | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider change this helper to:
void validate_ir_path(const std::filesystem::path& path, std::filesystem::path&& ext){
   OPENVINO_ASSERT(path.extension() == ext, "Path ",  path, " doesn't contains file name with ", ext,  " extension");
   OPENVINO_ASSERT(!is_path_traversal(path), "Path has traversal component: ", path);
   OPENVINO_ASSERT(!std::filesystem::is_symlink(path), "Path is symlink ", path);
}And use for xml, bin file validation, and additional check can be added here if required.
Details:
Tickets: