Skip to content

Improper handling of HDD paths by __path_absolute #805

@mlevogiannis

Description

@mlevogiannis

Description

__path_absolute (ee/libcglue/src/cwd.c) does not properly handle HDD paths, leading to all slashes being converted to backslashes during normalization.

One common case where __path_absolute is called with a full HDD path (e.g. hdd0:__common:pfs:/subdir) is by chdir during the initialization of PS2SDK, where __init_cwd calls chdir with argv[0] as argument. In that case, the CWD is not going to be hdd0:__common:pfs:/subdir, but hdd0:__common:pfs:\subdir, which is not a valid HDD path and will break applications that construct their paths from the CWD.

Root cause

__path_absolute calls __get_drive to infer the separator type and then __path_normalize with its result.

File: ee/libcglue/src/cwd.c

int __path_absolute(const char *in, char *out, int len)
{
...
    enum SeparatorType separatorType;
...
    /* See what the relative URL starts with */
    dr = __get_drive(in, &separatorType);
...
    if(dr > 0 && (separatorType == SeparatorTypeNone || in[dr] == separator)) {
        /* It starts with "drive:" and has no separator or "drive:/", so it's already absolute */
        if (in_len >= len) return -1;
        strncpy(out, in, len);
...
    /* Now normalize the pathname portion */
    dr = __get_drive(out, &separatorType);
...
    return __path_normalize(out + dr, len - dr, separatorType == SeparatorTypePOSIX);
...

__get_drive returns SeparatorTypeNone for paths starting with hdd.

File: ee/libcglue/src/cwd.c

int __get_drive(const char *dev, enum SeparatorType *usePOSIXSeparator)
{
...
    *usePOSIXSeparator = SeparatorTypePOSIX;
    switch (devname_len)
    {
    case 3:
        /* These drivers don't have separator */
        if ((memcmp(d, "rom", devname_len) == 0) || (memcmp(d, "hdd", devname_len) == 0))
            *usePOSIXSeparator = SeparatorTypeNone;
        break;
...

Consequently, __path_normalize is called (by __path_absolute, see above) with posixSeparator = 0.

File: ee/libcglue/src/cwd.c

static int __path_normalize(char *out, int len, int posixSeparator)
{
    char separator = posixSeparator ? '/' : '\\';
...
    /* First append "/" to make the rest easier */
    out[out_len - 1] = separator;
    out[out_len] = 0;

    // Convert separators to the specified one
    for (size_t i = 0; i < out_len; i++) {
        if (out[i] == '/' || out[i] == '\\') {
            out[i] = separator;
        }
    }
...

Since posixSeparator is 0, separator is set to \\ (backslash) and all slashes in the path are converted to backslashes.

Proof of Concept

The following is a short program that demonstrates the problem, using chdir to trigger the bug. Although you would not normally call chdir with a path starting with hdd0:, this is how it is called by __init_cwd when starting a program from an HDD partition.

#include <stdio.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    const char path[] = "hdd0:__common:pfs:/subdir";
    chdir(path);

    char cwd[PATH_MAX];
    getcwd(cwd, sizeof cwd);

    printf("path: %s\n", path);
    printf("cwd: %s\n", cwd);

    if (!strcmp(cwd, "hdd0:__common:pfs:\\subdir")) {
        printf("matches backslash\n");
    }

    char *p;
    if ((p = strchr(cwd, '\\')) != NULL) {
        printf("backslash at index: %i\n", p - cwd);
    }

    return 0;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions