diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc index 5b4e86807..a9aeb36bd 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc @@ -39,6 +39,41 @@ struct CreateOptions { static const uint32_t FILE_RANDOM_ACCESS = 0x00000800; }; +static bool IsValidPath(const std::string_view s, bool is_pattern) { + // TODO(gibbed): validate path components individually + for (const auto& c : s) { + if (c <= 31 || c >= 127) { + return false; + } + switch (c) { + case '"': + // case '*': + case '+': + case ',': + // case ':': + case ';': + case '<': + case '=': + case '>': + // case '?': + case '|': { + return false; + } + case '*': + case '?': { + // Pattern-specific (for NtQueryDirectoryFile) + if (!is_pattern) { + return false; + } + } + default: { + break; + } + } + } + return true; +} + dword_result_t NtCreateFile(lpdword_t handle_out, dword_t desired_access, pointer_t object_attrs, pointer_t io_status_block, @@ -64,6 +99,12 @@ dword_result_t NtCreateFile(lpdword_t handle_out, dword_t desired_access, // Compute path, possibly attrs relative. auto target_path = util::TranslateAnsiString(kernel_memory(), object_name); + + // Enforce that the path is ASCII. + if (!IsValidPath(target_path, false)) { + return X_STATUS_OBJECT_NAME_INVALID; + } + if (object_attrs->root_directory != 0xFFFFFFFD && // ObDosDevices object_attrs->root_directory != 0) { auto root_file = kernel_state()->object_table()->LookupObject( @@ -341,9 +382,15 @@ dword_result_t NtQueryFullAttributesFile( assert_always(); } + auto target_path = util::TranslateAnsiString(kernel_memory(), object_name); + + // Enforce that the path is ASCII. + if (!IsValidPath(target_path, false)) { + return X_STATUS_OBJECT_NAME_INVALID; + } + // Resolve the file using the virtual file system. - auto entry = kernel_state()->file_system()->ResolvePath( - util::TranslateAnsiString(kernel_memory(), object_name)); + auto entry = kernel_state()->file_system()->ResolvePath(target_path); if (entry) { // Found. file_info->creation_time = entry->create_timestamp(); @@ -375,6 +422,12 @@ dword_result_t NtQueryDirectoryFile( auto file = kernel_state()->object_table()->LookupObject(file_handle); auto name = util::TranslateAnsiString(kernel_memory(), file_name); + + // Enforce that the path is ASCII. + if (!IsValidPath(name, true)) { + return X_STATUS_OBJECT_NAME_INVALID; + } + if (file) { X_FILE_DIRECTORY_INFORMATION dir_info = {0}; result = @@ -427,6 +480,12 @@ dword_result_t NtOpenSymbolicLinkObject( std::string target_path = util::TranslateAnsiString(kernel_memory(), object_name); + + // Enforce that the path is ASCII. + if (!IsValidPath(target_path, false)) { + return X_STATUS_OBJECT_NAME_INVALID; + } + if (object_attrs->root_directory != 0) { assert_always(); } diff --git a/src/xenia/xbox.h b/src/xenia/xbox.h index 4556a2854..998def9ee 100644 --- a/src/xenia/xbox.h +++ b/src/xenia/xbox.h @@ -56,6 +56,7 @@ typedef uint32_t X_STATUS; #define X_STATUS_ACCESS_DENIED ((X_STATUS)0xC0000022L) #define X_STATUS_BUFFER_TOO_SMALL ((X_STATUS)0xC0000023L) #define X_STATUS_OBJECT_TYPE_MISMATCH ((X_STATUS)0xC0000024L) +#define X_STATUS_OBJECT_NAME_INVALID ((X_STATUS)0xC0000033L) #define X_STATUS_OBJECT_NAME_NOT_FOUND ((X_STATUS)0xC0000034L) #define X_STATUS_OBJECT_NAME_COLLISION ((X_STATUS)0xC0000035L) #define X_STATUS_INVALID_PAGE_PROTECTION ((X_STATUS)0xC0000045L)