ntdll: Add tests for buffer overflows in NtQueryDirectoryFile.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2016-04-28 16:44:25 +09:00
parent b0ce049cbb
commit a4dcfd1195
2 changed files with 157 additions and 23 deletions

View file

@ -2225,6 +2225,7 @@ NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event,
BOOLEAN restart_scan )
{
int cwd, fd, needs_close;
NTSTATUS status;
TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n",
handle, event, apc_routine, apc_context, io, buffer,
@ -2234,7 +2235,7 @@ NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event,
if (event || apc_routine)
{
FIXME( "Unsupported yet option\n" );
return io->u.Status = STATUS_NOT_IMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}
switch (info_class)
{
@ -2243,16 +2244,16 @@ NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event,
case FileFullDirectoryInformation:
case FileIdBothDirectoryInformation:
case FileIdFullDirectoryInformation:
if (length < dir_info_size( info_class, 1 )) return io->u.Status = STATUS_INFO_LENGTH_MISMATCH;
if (!buffer) return io->u.Status = STATUS_ACCESS_VIOLATION;
if (length < dir_info_size( info_class, 1 )) return STATUS_INFO_LENGTH_MISMATCH;
if (!buffer) return STATUS_ACCESS_VIOLATION;
break;
default:
FIXME( "Unsupported file info class %d\n", info_class );
return io->u.Status = STATUS_NOT_IMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}
if ((io->u.Status = server_get_unix_fd( handle, FILE_LIST_DIRECTORY, &fd, &needs_close, NULL, NULL )) != STATUS_SUCCESS)
return io->u.Status;
if ((status = server_get_unix_fd( handle, FILE_LIST_DIRECTORY, &fd, &needs_close, NULL, NULL )) != STATUS_SUCCESS)
return status;
io->Information = 0;
@ -2290,16 +2291,17 @@ NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event,
read_directory_readdir( fd, io, buffer, length, single_entry, mask, restart_scan, info_class );
done:
status = io->u.Status;
if (cwd == -1 || fchdir( cwd ) == -1) chdir( "/" );
}
else io->u.Status = FILE_GetNtStatus();
else status = FILE_GetNtStatus();
RtlLeaveCriticalSection( &dir_section );
if (needs_close) close( fd );
if (cwd != -1) close( cwd );
TRACE( "=> %x (%ld)\n", io->u.Status, io->Information );
return io->u.Status;
TRACE( "=> %x (%ld)\n", status, io->Information );
return status;
}

View file

@ -168,7 +168,7 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char
UINT data_len; /* length of dir data */
BYTE data[8192]; /* directory data */
FILE_BOTH_DIRECTORY_INFORMATION *dir_info;
DWORD status;
NTSTATUS status;
int numfiles;
int i;
@ -185,8 +185,10 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char
return;
}
pNtQueryDirectoryFile( dirh, NULL, NULL, NULL, &io, data, data_size,
FileBothDirectoryInformation, single_entry, mask, restart_flag );
U(io).Status = 0xdeadbeef;
status = pNtQueryDirectoryFile( dirh, NULL, NULL, NULL, &io, data, data_size,
FileBothDirectoryInformation, single_entry, mask, restart_flag );
ok (status == STATUS_SUCCESS, "failed to query directory; status %x\n", status);
ok (U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", U(io).Status);
data_len = io.Information;
ok (data_len >= sizeof(FILE_BOTH_DIRECTORY_INFORMATION), "not enough data in directory\n");
@ -199,11 +201,12 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char
tally_test_file(dir_info);
if (dir_info->NextEntryOffset == 0) {
pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size,
FileBothDirectoryInformation, single_entry, mask, FALSE );
if (U(io).Status == STATUS_NO_MORE_FILES)
break;
ok (U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", U(io).Status);
U(io).Status = 0xdeadbeef;
status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size,
FileBothDirectoryInformation, single_entry, mask, FALSE );
ok (U(io).Status == status, "wrong status %x / %x\n", status, U(io).Status);
if (status == STATUS_NO_MORE_FILES) break;
ok (status == STATUS_SUCCESS, "failed to query directory; status %x\n", status);
data_len = io.Information;
if (data_len < sizeof(FILE_BOTH_DIRECTORY_INFORMATION))
break;
@ -239,8 +242,9 @@ static void test_NtQueryDirectoryFile(void)
WCHAR short_name[12];
UINT data_size;
BYTE data[8192];
FILE_BOTH_DIRECTORY_INFORMATION *fbdi = (FILE_BOTH_DIRECTORY_INFORMATION*)data;
DWORD status;
FILE_BOTH_DIRECTORY_INFORMATION *next, *fbdi = (FILE_BOTH_DIRECTORY_INFORMATION*)data;
const WCHAR *filename = fbdi->FileName;
NTSTATUS status;
HANDLE dirh;
/* Clean up from prior aborted run, if any, then set up test files */
@ -284,24 +288,152 @@ static void test_NtQueryDirectoryFile(void)
mask.Buffer = testfiles[0].nameW;
mask.Length = mask.MaximumLength = lstrlenW(testfiles[0].nameW) * sizeof(WCHAR);
data_size = offsetof(FILE_BOTH_DIRECTORY_INFORMATION, FileName[256]);
pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
FileBothDirectoryInformation, TRUE, &mask, FALSE);
U(io).Status = 0xdeadbeef;
status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
FileBothDirectoryInformation, TRUE, &mask, FALSE);
ok(status == STATUS_SUCCESS, "failed to query directory; status %x\n", status);
ok(U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", U(io).Status);
ok(fbdi->ShortName[0], "ShortName is empty\n");
mask.Length = mask.MaximumLength = fbdi->ShortNameLength;
memcpy(short_name, fbdi->ShortName, mask.Length);
mask.Buffer = short_name;
pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
FileBothDirectoryInformation, TRUE, &mask, TRUE);
U(io).Status = 0xdeadbeef;
U(io).Information = 0xdeadbeef;
status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
FileBothDirectoryInformation, TRUE, &mask, TRUE);
ok(status == STATUS_SUCCESS, "failed to query directory status %x\n", status);
ok(U(io).Status == STATUS_SUCCESS, "failed to query directory status %x\n", U(io).Status);
todo_wine
ok(U(io).Information == offsetof(FILE_BOTH_DIRECTORY_INFORMATION, FileName[strlen(testfiles[0].name)]),
"wrong info %lx\n", U(io).Information);
ok(fbdi->FileNameLength == strlen(testfiles[0].name)*sizeof(WCHAR) &&
!memcmp(fbdi->FileName, testfiles[0].nameW, fbdi->FileNameLength),
"incorrect long file name: %s\n", wine_dbgstr_wn(fbdi->FileName,
fbdi->FileNameLength/sizeof(WCHAR)));
/* tests with short buffer */
memset( data, 0x55, data_size );
U(io).Status = 0xdeadbeef;
U(io).Information = 0xdeadbeef;
data_size = offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] );
status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
FileBothDirectoryInformation, TRUE, &mask, TRUE);
ok( status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", status );
ok( U(io).Status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", U(io).Status );
ok( U(io).Information == offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ),
"wrong info %lx\n", U(io).Information );
ok( fbdi->NextEntryOffset == 0, "wrong offset %x\n", fbdi->NextEntryOffset );
ok( fbdi->FileNameLength == strlen(testfiles[0].name) * sizeof(WCHAR),
"wrong length %x\n", fbdi->FileNameLength );
ok( filename[0] == testfiles[0].nameW[0], "incorrect long file name: %s\n",
wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR)));
todo_wine
ok( filename[1] == 0x5555, "incorrect long file name: %s\n",
wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR)));
memset( data, 0x55, data_size );
U(io).Status = 0xdeadbeef;
U(io).Information = 0xdeadbeef;
data_size = offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[0] );
status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
FileBothDirectoryInformation, FALSE, &mask, TRUE);
ok( status == STATUS_INFO_LENGTH_MISMATCH, "weong status %x\n", status );
ok( U(io).Status == 0xdeadbeef, "wrong status %x\n", U(io).Status );
ok( U(io).Information == 0xdeadbeef, "wrong info %lx\n", U(io).Information );
ok( fbdi->NextEntryOffset == 0x55555555, "wrong offset %x\n", fbdi->NextEntryOffset );
pNtClose(dirh);
status = pNtOpenFile(&dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE);
ok(status == STATUS_SUCCESS, "failed to open dir '%s'\n", testdirA);
memset( data, 0x55, data_size );
data_size = sizeof(data);
U(io).Status = 0xdeadbeef;
U(io).Information = 0xdeadbeef;
status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
FileBothDirectoryInformation, FALSE, NULL, TRUE);
ok(status == STATUS_SUCCESS, "wrong status %x\n", status);
ok(U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status);
ok(U(io).Information > 0 && U(io).Information < data_size, "wrong info %lx\n", U(io).Information);
ok( fbdi->NextEntryOffset == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7),
"wrong offset %x\n", fbdi->NextEntryOffset );
ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %x\n", fbdi->FileNameLength );
ok( fbdi->FileName[0] == '.', "incorrect long file name: %s\n",
wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR)));
next = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + fbdi->NextEntryOffset);
ok( next->NextEntryOffset == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[2] ) + 7) & ~7),
"wrong offset %x\n", next->NextEntryOffset );
ok( next->FileNameLength == 2 * sizeof(WCHAR), "wrong length %x\n", next->FileNameLength );
filename = next->FileName;
ok( filename[0] == '.' && filename[1] == '.', "incorrect long file name: %s\n",
wine_dbgstr_wn(next->FileName, next->FileNameLength/sizeof(WCHAR)));
data_size = fbdi->NextEntryOffset + offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ),
memset( data, 0x55, data_size );
U(io).Status = 0xdeadbeef;
U(io).Information = 0xdeadbeef;
status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size,
FileBothDirectoryInformation, FALSE, NULL, TRUE );
ok( status == STATUS_SUCCESS, "wrong status %x\n", status );
ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status );
ok( U(io).Information == offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ),
"wrong info %lx\n", U(io).Information );
ok( fbdi->NextEntryOffset == 0, "wrong offset %x\n", fbdi->NextEntryOffset );
ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %x\n", fbdi->FileNameLength );
ok( fbdi->FileName[0] == '.', "incorrect long file name: %s\n",
wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR)));
next = (FILE_BOTH_DIRECTORY_INFORMATION *)&fbdi->FileName[1];
ok( next->NextEntryOffset == 0x55555555, "wrong offset %x\n", next->NextEntryOffset );
data_size = fbdi->NextEntryOffset + offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[2] ),
memset( data, 0x55, data_size );
U(io).Status = 0xdeadbeef;
U(io).Information = 0xdeadbeef;
status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size,
FileBothDirectoryInformation, FALSE, NULL, TRUE );
ok( status == STATUS_SUCCESS, "wrong status %x\n", status );
ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status );
ok( U(io).Information == offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ),
"wrong info %lx\n", U(io).Information );
ok( fbdi->NextEntryOffset == 0, "wrong offset %x\n", fbdi->NextEntryOffset );
data_size = ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7) +
offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[2] );
memset( data, 0x55, data_size );
U(io).Status = 0xdeadbeef;
U(io).Information = 0xdeadbeef;
status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size,
FileBothDirectoryInformation, FALSE, NULL, TRUE );
ok( status == STATUS_SUCCESS, "wrong status %x\n", status );
ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status );
todo_wine
ok( U(io).Information == data_size, "wrong info %lx / %x\n", U(io).Information, data_size );
todo_wine
ok( fbdi->NextEntryOffset == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7),
"wrong offset %x\n", fbdi->NextEntryOffset );
ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %x\n", fbdi->FileNameLength );
ok( fbdi->FileName[0] == '.', "incorrect long file name: %s\n",
wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR)));
next = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + fbdi->NextEntryOffset);
ok( next->NextEntryOffset == 0, "wrong offset %x\n", next->NextEntryOffset );
todo_wine
ok( next->FileNameLength == 2 * sizeof(WCHAR), "wrong length %x\n", next->FileNameLength );
filename = next->FileName;
todo_wine
ok( filename[0] == '.' && filename[1] == '.', "incorrect long file name: %s\n",
wine_dbgstr_wn(next->FileName, next->FileNameLength/sizeof(WCHAR)));
pNtClose(dirh);
U(io).Status = 0xdeadbeef;
status = pNtQueryDirectoryFile( (HANDLE)0xbeef, 0, NULL, NULL, &io, data, data_size,
FileBothDirectoryInformation, TRUE, NULL, TRUE );
ok(status == STATUS_INVALID_HANDLE, "wrong status %x\n", status);
ok(U(io).Status == 0xdeadbeef, "wrong status %x\n", U(io).Status);
done:
tear_down_attribute_test(testdirA);
pRtlFreeUnicodeString(&ntdirname);