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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions