Skip to content

[Code scan] Treat short POSIX restart reads and writes as failures #7561

Description

@njzjz

This issue is a result of a Codex global repository scan.

The binary restart helpers call POSIX write() and read() once and only treat -1 as failure. POSIX allows a positive return smaller than the requested byte count. In that case these helpers currently report success even though only part of the restart payload was transferred.

bool Restart::write_file2(const std::string& file_name, const void* const ptr, const size_t size, const bool error_quit) const
{
const int file = open(file_name.c_str(), O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
if (-1 == file){
if (error_quit){
throw std::runtime_error("can't open restart save file. \nerrno=" + ModuleBase::GlobalFunc::TO_STRING(errno) + ".\n" + std::string(__FILE__) + " line " + std::to_string(__LINE__));
} else {
return false;
}
}
auto error = write(file, ptr, size);
if (-1 == error) {
if (error_quit) {
throw std::runtime_error("can't write restart save file. \nerrno=" + ModuleBase::GlobalFunc::TO_STRING(errno) + ".\n" + std::string(__FILE__) + " line " + std::to_string(__LINE__));
} else {
return false;
}
}
close(file);
return true;

bool Restart::read_file2(const std::string& file_name, void* const ptr, const size_t size, const bool error_quit) const
{
const int file = open(file_name.c_str(), O_RDONLY);
if (-1 == file) {
if (error_quit) {
throw std::runtime_error("can't open restart load file. \nerrno=" + ModuleBase::GlobalFunc::TO_STRING(errno) + ".\n" + std::string(__FILE__) + " line " + std::to_string(__LINE__));
} else {
return false;
}
}
auto error = read(file, ptr, size);
if (-1 == error) {
if (error_quit) {
throw std::runtime_error("can't read restart load file. \nerrno=" + ModuleBase::GlobalFunc::TO_STRING(errno) + ".\n" + std::string(__FILE__) + " line " + std::to_string(__LINE__));
} else {
return false;
}
}
close(file);
return true;

Caller-facing byte count construction:

template<typename T>
bool save_disk(const std::string label, const int index, const int size, T* data, const bool error_quit = true) const
{
return write_file2(folder + label + "_" + ModuleBase::GlobalFunc::TO_STRING(GlobalV::MY_RANK) + "_"
+ ModuleBase::GlobalFunc::TO_STRING(index),
data,
size * sizeof(T),

Relevant code:

auto error = write(file, ptr, size);
if (-1 == error) { ... }
...
auto error = read(file, ptr, size);
if (-1 == error) { ... }

Impact:

A truncated restart shard can be accepted as successfully loaded, leaving part of the destination buffer stale and making restart behavior non-deterministic.

Suggested fix:

Loop until exactly size bytes are transferred, or return/throw on EOF or a short write. Use RAII for the file descriptor so open descriptors are closed on all error paths.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions