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 ) BOOLEAN restart_scan )
{ {
int cwd, fd, needs_close; int cwd, fd, needs_close;
NTSTATUS status;
TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n", 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, handle, event, apc_routine, apc_context, io, buffer,
@ -2234,7 +2235,7 @@ NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event,
if (event || apc_routine) if (event || apc_routine)
{ {
FIXME( "Unsupported yet option\n" ); FIXME( "Unsupported yet option\n" );
return io->u.Status = STATUS_NOT_IMPLEMENTED; return STATUS_NOT_IMPLEMENTED;
} }
switch (info_class) switch (info_class)
{ {
@ -2243,16 +2244,16 @@ NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event,
case FileFullDirectoryInformation: case FileFullDirectoryInformation:
case FileIdBothDirectoryInformation: case FileIdBothDirectoryInformation:
case FileIdFullDirectoryInformation: case FileIdFullDirectoryInformation:
if (length < dir_info_size( info_class, 1 )) return io->u.Status = STATUS_INFO_LENGTH_MISMATCH; if (length < dir_info_size( info_class, 1 )) return STATUS_INFO_LENGTH_MISMATCH;
if (!buffer) return io->u.Status = STATUS_ACCESS_VIOLATION; if (!buffer) return STATUS_ACCESS_VIOLATION;
break; break;
default: default:
FIXME( "Unsupported file info class %d\n", info_class ); 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) if ((status = server_get_unix_fd( handle, FILE_LIST_DIRECTORY, &fd, &needs_close, NULL, NULL )) != STATUS_SUCCESS)
return io->u.Status; return status;
io->Information = 0; 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 ); read_directory_readdir( fd, io, buffer, length, single_entry, mask, restart_scan, info_class );
done: done:
status = io->u.Status;
if (cwd == -1 || fchdir( cwd ) == -1) chdir( "/" ); if (cwd == -1 || fchdir( cwd ) == -1) chdir( "/" );
} }
else io->u.Status = FILE_GetNtStatus(); else status = FILE_GetNtStatus();
RtlLeaveCriticalSection( &dir_section ); RtlLeaveCriticalSection( &dir_section );
if (needs_close) close( fd ); if (needs_close) close( fd );
if (cwd != -1) close( cwd ); if (cwd != -1) close( cwd );
TRACE( "=> %x (%ld)\n", io->u.Status, io->Information ); TRACE( "=> %x (%ld)\n", status, io->Information );
return io->u.Status; 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 */ UINT data_len; /* length of dir data */
BYTE data[8192]; /* directory data */ BYTE data[8192]; /* directory data */
FILE_BOTH_DIRECTORY_INFORMATION *dir_info; FILE_BOTH_DIRECTORY_INFORMATION *dir_info;
DWORD status; NTSTATUS status;
int numfiles; int numfiles;
int i; int i;
@ -185,8 +185,10 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char
return; return;
} }
pNtQueryDirectoryFile( dirh, NULL, NULL, NULL, &io, data, data_size, U(io).Status = 0xdeadbeef;
FileBothDirectoryInformation, single_entry, mask, restart_flag ); 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); ok (U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", U(io).Status);
data_len = io.Information; data_len = io.Information;
ok (data_len >= sizeof(FILE_BOTH_DIRECTORY_INFORMATION), "not enough data in directory\n"); 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); tally_test_file(dir_info);
if (dir_info->NextEntryOffset == 0) { if (dir_info->NextEntryOffset == 0) {
pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size, U(io).Status = 0xdeadbeef;
FileBothDirectoryInformation, single_entry, mask, FALSE ); status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size,
if (U(io).Status == STATUS_NO_MORE_FILES) FileBothDirectoryInformation, single_entry, mask, FALSE );
break; ok (U(io).Status == status, "wrong status %x / %x\n", status, U(io).Status);
ok (U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", 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; data_len = io.Information;
if (data_len < sizeof(FILE_BOTH_DIRECTORY_INFORMATION)) if (data_len < sizeof(FILE_BOTH_DIRECTORY_INFORMATION))
break; break;
@ -239,8 +242,9 @@ static void test_NtQueryDirectoryFile(void)
WCHAR short_name[12]; WCHAR short_name[12];
UINT data_size; UINT data_size;
BYTE data[8192]; BYTE data[8192];
FILE_BOTH_DIRECTORY_INFORMATION *fbdi = (FILE_BOTH_DIRECTORY_INFORMATION*)data; FILE_BOTH_DIRECTORY_INFORMATION *next, *fbdi = (FILE_BOTH_DIRECTORY_INFORMATION*)data;
DWORD status; const WCHAR *filename = fbdi->FileName;
NTSTATUS status;
HANDLE dirh; HANDLE dirh;
/* Clean up from prior aborted run, if any, then set up test files */ /* 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.Buffer = testfiles[0].nameW;
mask.Length = mask.MaximumLength = lstrlenW(testfiles[0].nameW) * sizeof(WCHAR); mask.Length = mask.MaximumLength = lstrlenW(testfiles[0].nameW) * sizeof(WCHAR);
data_size = offsetof(FILE_BOTH_DIRECTORY_INFORMATION, FileName[256]); data_size = offsetof(FILE_BOTH_DIRECTORY_INFORMATION, FileName[256]);
pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, U(io).Status = 0xdeadbeef;
FileBothDirectoryInformation, TRUE, &mask, FALSE); 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(U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", U(io).Status);
ok(fbdi->ShortName[0], "ShortName is empty\n"); ok(fbdi->ShortName[0], "ShortName is empty\n");
mask.Length = mask.MaximumLength = fbdi->ShortNameLength; mask.Length = mask.MaximumLength = fbdi->ShortNameLength;
memcpy(short_name, fbdi->ShortName, mask.Length); memcpy(short_name, fbdi->ShortName, mask.Length);
mask.Buffer = short_name; mask.Buffer = short_name;
pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, U(io).Status = 0xdeadbeef;
FileBothDirectoryInformation, TRUE, &mask, TRUE); 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); 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) && ok(fbdi->FileNameLength == strlen(testfiles[0].name)*sizeof(WCHAR) &&
!memcmp(fbdi->FileName, testfiles[0].nameW, fbdi->FileNameLength), !memcmp(fbdi->FileName, testfiles[0].nameW, fbdi->FileNameLength),
"incorrect long file name: %s\n", wine_dbgstr_wn(fbdi->FileName, "incorrect long file name: %s\n", wine_dbgstr_wn(fbdi->FileName,
fbdi->FileNameLength/sizeof(WCHAR))); 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); 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: done:
tear_down_attribute_test(testdirA); tear_down_attribute_test(testdirA);
pRtlFreeUnicodeString(&ntdirname); pRtlFreeUnicodeString(&ntdirname);