-
Notifications
You must be signed in to change notification settings - Fork 156
Description
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;
}