Skip to content
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

Bug Report: Infinite Loop in create_directory_recursive Method When Directory Location is Unreachable #22

Open
supriya-yadav12 opened this issue Sep 9, 2024 · 0 comments

Comments

@supriya-yadav12
Copy link

supriya-yadav12 commented Sep 9, 2024

Issue Summary:

The code enters into an infinite loop while trying to create directories recursively if the target directory location is unreachable or inaccessible. This issue occurs differently on Windows and Unix-based systems.

Affected Method:

The issue is found in the create_directory_recursive method, specifically in the while loop that checks if a directory exists. The loop fails to terminate properly when the directory path is invalid or inaccessible.

bool create_directory_recursive(std::string path) {
    std::string tmp = absolute_path(path);
    std::string target = tmp;

    // Issue with this while loop.
    while (!is_directory(tmp)) {
        if (tmp.empty()) return false;  // invalid path
        tmp = absolute_path(tmp + "/../");
    }

    // tmp is the root from where to **build**
    auto list = path_split(fmt::lstrip(target, tmp));
    for (auto sub : list) {
        tmp = path_join({ tmp, sub });
        if (!create_directory(tmp)) break;
    }
    return is_directory(path);
}

Steps to Reproduce:
Attempt to create directories recursively for an inaccessible or unreachable path (e.g., due to permission issues).
Observe that the process enters into an infinite loop due to repetitive failure in reaching the target directory.

IN UNIX BASED SYSTEMS

Detailed Description:
On Unix-based systems, when the location is unreachable, the code attempts to fetch the absolute path in each iteration. If the directory is inaccessible (e.g., due to permission issues, errno == EACCES), the code appends "/../" to the path and retries, leading to the path growing indefinitely.

Example flow with a path like User/admin/a/b/:

If the directory check (is_directory) fails with EACCES, the code will append "/../" to the path, resulting in User/admin/a/b/../.
The absolute_path function will then return the path as it is since it can't resolve it.
This leads to the code being stuck in a loop, retrying with an ever-growing path and never succeeding.

std::string absolute_path(std::string relativePath) {
#if ZUPPLY_OS_UNIX
    char *buffer = realpath(relativePath.c_str(), nullptr);
    if (buffer == nullptr) {

        if (ENOENT == errno) { // Issue due to this check.
            // Try to manually resolve the path
            std::string dirtyPath;
            if (fmt::starts_with(relativePath, "/")) {
                dirtyPath = relativePath;
            } else {
                dirtyPath = path_join({ current_working_directory(), relativePath });
            }

            std::vector<std::string> parts = path_split(dirtyPath);
            std::vector<std::string> ret;
            for (auto i = parts.begin(); i != parts.end(); ++i) {
                if (*i == ".") continue;
                if (*i == "..") {
                    if (ret.size() < 1) throw RuntimeException("Invalid path: " + dirtyPath);
                    ret.pop_back();
                } else {
                    ret.push_back(*i);
                }
            }
            std::string tmp = path_join(ret);
            if (!fmt::starts_with(tmp, "/")) tmp = "/" + tmp;
            return tmp;
        }
        log::detail::zupply_internal_warn("Unable to get absolute path for: " + relativePath + "! Return original.");
        return relativePath; / The passed parameter is returned without getting modified.
    } else {
        std::string ret(buffer);
        free(buffer);
        return ret;
    }
#endif
    return std::string(); 
}

Root Cause:
When the target directory is unreachable or permission is denied (i.e., errno == EACCES), the path keeps growing with "/../" added during each iteration, causing an infinite loop as the absolute_path method returns the passed path in parameter if the error code is not ENOENT.

IN WINDOWS

Detailed Description:
On Windows, when the target location is unreachable, the code tries to fetch the absolute path in each iteration. If the directory is inaccessible, the loop eventually reaches the root directory (e.g., Z:/). The code then appends "/../" to the path and retries, which leads to repeatedly returning root path.

Example flow with a path like Z:/user/a/b/:

If the directory check (is_directory) fails, the code appends "/../" to the path, resulting in Z:/user/a/b//../. After several iterations, the path reaches the root, such as Z://../, which resolves back to Z:/. This causes the loop to get stuck in a cycle, repeatedly attempting with Z:/, without ever succeeding.

std::string absolute_path(std::string reletivePath) {
#if ZUPPLY_OS_WINDOWS
    wchar_t *buffer = nullptr;
    std::wstring widePath = utf8_to_wstring(reletivePath);
    buffer = _wfullpath(buffer, widePath.c_str(), _MAX_PATH);
    if (buffer == nullptr) {
        // failed
        log::detail::zupply_internal_warn("Unable to get absolute path for: " + reletivePath + "! Return original.");
        return reletivePath;
    } else {
        std::wstring ret(buffer);
        free(buffer);
        return wstring_to_utf8(ret);
    }
#endif
    return std::string();
}

Root Cause:
When the directory is inaccessible, the code continuously appends "/../" to the path, attempting to move up the directory hierarchy. This eventually leads to the root directory (e.g., Z:/). At the root level, appending "/../" has no further effect, as the absolute path still resolves to Z:/. The code enters an infinite loop, repeatedly attempting to access the root directory without successfully breaking the cycle.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant