Fix for App crash when File class receive an empty string

TEST=new test added
Change-Id: I575d85c5ab13a2094114a6fa2a7fe9f06ebeabe4
FIXES: https://github.com/dart-lang/sdk/issues/60331
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/417962
Reviewed-by: Brian Quinlan <bquinlan@google.com>
This commit is contained in:
asiva
2025-03-26 21:26:49 -07:00
committed by Siva Annamalai
parent 2134ac9385
commit 8af371c1e8
2 changed files with 371 additions and 5 deletions
+58 -5
View File
@@ -406,6 +406,10 @@ std::unique_ptr<wchar_t[]> ToWinAPIPath(const char* utf8_path) {
File* File::Open(Namespace* namespc, const char* name, FileOpenMode mode) {
const auto path = ToWinAPIPath(name);
if (path.get() == nullptr) {
SetLastError(ERROR_INVALID_NAME);
return nullptr;
}
File* file = FileOpenW(path.get(), mode);
return file;
}
@@ -476,6 +480,9 @@ static bool FileExists(const wchar_t* path) {
bool File::Exists(Namespace* namespc, const char* name) {
const auto path = ToWinAPIPath(name);
if (path.get() == nullptr) {
return false;
}
return FileExists(path.get());
}
@@ -490,6 +497,9 @@ bool File::ExistsUri(Namespace* namespc, const char* uri) {
bool File::Create(Namespace* namespc, const char* name, bool exclusive) {
const auto path = ToWinAPIPath(name);
if (path.get() == nullptr) {
return false;
}
int flags = O_RDONLY | O_CREAT;
if (exclusive) {
flags |= O_EXCL;
@@ -535,11 +545,19 @@ bool File::CreateLink(Namespace* namespc,
const char* utf8_name,
const char* utf8_target) {
const auto name = ToWinAPIPath(utf8_name);
if (name.get() == nullptr) {
SetLastError(ERROR_INVALID_NAME);
return false;
}
std::unique_ptr<wchar_t[]> target;
bool target_is_directory;
if (File::IsAbsolutePath(utf8_target)) {
target = ToWinAPIPath(utf8_target);
if (target.get() == nullptr) {
SetLastError(ERROR_INVALID_NAME);
return false;
}
target_is_directory =
File::GetType(target.get(), /*follow_links=*/true) == kIsDirectory;
} else {
@@ -625,6 +643,9 @@ bool File::CreatePipe(Namespace* namespc, File** readPipe, File** writePipe) {
bool File::Delete(Namespace* namespc, const char* name) {
const auto path = ToWinAPIPath(name);
if (path.get() == nullptr) {
return false;
}
int status = _wremove(path.get());
return status != -1;
}
@@ -650,6 +671,9 @@ static bool DeleteLinkHelper(const wchar_t* path) {
bool File::DeleteLink(Namespace* namespc, const char* name) {
const auto path = ToWinAPIPath(name);
if (path.get() == nullptr) {
return false;
}
return DeleteLinkHelper(path.get());
}
@@ -657,12 +681,20 @@ static bool RenameHelper(File::Type expected,
const char* old_name,
const char* new_name) {
const auto old_path = ToWinAPIPath(old_name);
if (old_path.get() == nullptr) {
SetLastError(ERROR_INVALID_NAME);
return false;
}
File::Type type = File::GetType(old_path.get(), /*follow_links=*/false);
if (type != expected) {
SetLastError(ERROR_FILE_NOT_FOUND);
return false;
}
const auto new_path = ToWinAPIPath(new_name);
if (new_path.get() == nullptr) {
SetLastError(ERROR_INVALID_NAME);
return false;
}
DWORD flags = MOVEFILE_WRITE_THROUGH | MOVEFILE_REPLACE_EXISTING;
// Symbolic links (e.g. produced by Link.create) to directories on Windows
@@ -780,6 +812,10 @@ bool File::Copy(Namespace* namespc,
// CopyIntoTempFile so we force long prefix no matter what.
const auto old_path = ToWinAPIPath(old_name);
const auto new_path = ToWinAPIPath(new_name);
if (new_path.get() == nullptr || old_path.get() == nullptr) {
SetLastError(ERROR_INVALID_NAME);
return false;
}
File::Type type = GetType(old_path.get(), /*follow_links=*/false);
if (type != kIsFile) {
@@ -812,7 +848,7 @@ bool File::Copy(Namespace* namespc,
int64_t File::LengthFromPath(Namespace* namespc, const char* name) {
struct __stat64 st;
const auto path = ToWinAPIPath(name);
if (!StatHelper(path.get(), &st)) {
if (path.get() == nullptr || !StatHelper(path.get(), &st)) {
return -1;
}
return st.st_size;
@@ -823,6 +859,9 @@ const char* File::LinkTarget(Namespace* namespc,
char* dest,
int dest_size) {
const auto path = ToWinAPIPath(pathname);
if (path.get() == nullptr) {
return nullptr;
}
HANDLE dir_handle = CreateFileW(
path.get(), GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
@@ -910,6 +949,10 @@ const char* File::LinkTarget(Namespace* namespc,
void File::Stat(Namespace* namespc, const char* name, int64_t* data) {
const auto path = ToWinAPIPath(name);
if (path.get() == nullptr) {
data[kType] = File::kDoesNotExist;
return;
}
File::Type type = GetType(path.get(), /*follow_links=*/true);
data[kType] = type;
if (type != kDoesNotExist) {
@@ -930,7 +973,7 @@ void File::Stat(Namespace* namespc, const char* name, int64_t* data) {
time_t File::LastAccessed(Namespace* namespc, const char* name) {
struct __stat64 st;
const auto path = ToWinAPIPath(name);
if (!StatHelper(path.get(), &st)) {
if (path.get() == nullptr || !StatHelper(path.get(), &st)) {
return -1;
}
return st.st_atime;
@@ -939,7 +982,7 @@ time_t File::LastAccessed(Namespace* namespc, const char* name) {
time_t File::LastModified(Namespace* namespc, const char* name) {
struct __stat64 st;
const auto path = ToWinAPIPath(name);
if (!StatHelper(path.get(), &st)) {
if (path.get() == nullptr || !StatHelper(path.get(), &st)) {
return -1;
}
return st.st_mtime;
@@ -950,7 +993,7 @@ bool File::SetLastAccessed(Namespace* namespc,
int64_t millis) {
struct __stat64 st;
const auto path = ToWinAPIPath(name);
if (!StatHelper(path.get(), &st)) { // Checks that it is a file.
if (path.get() == nullptr || !StatHelper(path.get(), &st)) {
return false;
}
@@ -979,7 +1022,7 @@ bool File::SetLastModified(Namespace* namespc,
// First get the current times.
struct __stat64 st;
const auto path = ToWinAPIPath(name);
if (!StatHelper(path.get(), &st)) {
if (path.get() == nullptr || !StatHelper(path.get(), &st)) {
return false;
}
@@ -1008,6 +1051,10 @@ const char* File::GetCanonicalPath(Namespace* namespc,
char* dest,
int dest_size) {
const auto path = ToWinAPIPath(pathname);
if (path.get() == nullptr) {
SetLastError(ERROR_INVALID_NAME);
return nullptr;
}
HANDLE file_handle =
CreateFileW(path.get(), 0, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, nullptr);
@@ -1110,6 +1157,9 @@ File::Type File::GetType(Namespace* namespc,
const char* name,
bool follow_links) {
const auto path = ToWinAPIPath(name);
if (path.get() == nullptr) {
return File::kDoesNotExist;
}
return GetType(path.get(), follow_links);
}
@@ -1122,6 +1172,9 @@ File::Identical File::AreIdentical(Namespace* namespc_1,
BY_HANDLE_FILE_INFORMATION file_info[2];
const std::unique_ptr<wchar_t[]> file_names[2] = {ToWinAPIPath(file_1),
ToWinAPIPath(file_2)};
if (file_names[0].get() == nullptr || file_names[1].get() == nullptr) {
return File::kError;
}
for (int i = 0; i < 2; ++i) {
HANDLE file_handle = CreateFileW(
file_names[i].get(), 0,
+313
View File
@@ -0,0 +1,313 @@
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
//
// Dart test program for testing empty file pathname parameter.
import 'dart:io';
import "package:expect/async_helper.dart";
import "package:expect/expect.dart";
Future<void> testOpen() async {
try {
await File('').open();
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
}
Future<void> testExists() async {
try {
var retval = await File('').exists();
Expect.equals(retval, false);
} catch (e) {
Expect.equals(1, 0);
}
try {
var retval = File('').existsSync();
Expect.equals(retval, false);
} catch (e) {
Expect.equals(1, 0);
}
}
Future<void> testCreate() async {
try {
await File('').create();
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
File('').createSync();
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
}
Future<void> testCreateLink() async {
try {
await Link('').create('test');
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
await Link('test').create('');
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
Link('').createSync('test');
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
Link('test').createSync('');
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
}
Future<void> testDelete() async {
try {
await File('').delete();
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
File('').deleteSync();
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
}
Future<void> testDeleteLink() async {
try {
await Link('').delete();
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
Link('').deleteSync();
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
}
Future<void> testRename() async {
try {
await File('').rename('test');
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
late File file;
Directory directory = Directory.systemTemp.createTempSync(
'dart_test_directory',
);
try {
file = File('${directory.path}/test');
file.createSync();
await file.rename('');
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
} finally {
file.deleteSync();
}
try {
File('').renameSync('test');
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
file = File('${directory.path}/test');
file.createSync();
file.renameSync('');
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
} finally {
file.deleteSync();
}
directory.deleteSync(recursive: true);
}
Future<void> testCopy() async {
try {
await File('').copy('test');
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
await File('test').copy('');
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
File('').copySync('test');
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
File('test').copySync('');
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
}
Future<void> testLength() async {
try {
var len = await File('').length();
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
var len = File('').lengthSync();
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
}
Future<void> testStat() async {
final now = DateTime.now();
try {
var len = await File('').stat();
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
var len = File('').statSync();
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
var len = await File('').lastAccessed();
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
var len = File('').lastAccessedSync();
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
var len = await File('').setLastAccessed(now);
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
var len = File('').setLastAccessedSync(now);
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
var len = await File('').lastModified();
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
var len = File('').lastModifiedSync();
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
var len = await File('').setLastModified(now);
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
var len = File('').setLastModifiedSync(now);
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
}
Future<void> testResolveSymbolicLinks() async {
try {
var str = await File('').resolveSymbolicLinks();
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
var str = File('').resolveSymbolicLinksSync();
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
}
Future<void> testType() async {
try {
var val = await FileSystemEntity.isDirectory('');
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
var val = FileSystemEntity.isDirectorySync('');
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
}
Future<void> testIdentical() async {
try {
var val = await FileSystemEntity.identical('', 'test');
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
try {
var val = FileSystemEntity.identicalSync('test', '');
Expect.equals(1, 0);
} catch (e) {
Expect.equals(1, 1);
}
}
main() async {
await testOpen();
await testExists();
await testCreate();
await testCreateLink();
await testDelete();
await testDeleteLink();
await testRename();
await testCopy();
await testLength();
await testStat();
await testResolveSymbolicLinks();
await testType();
await testIdentical();
}