From a4dcfd11952067fa0e291645757c643e8fb73801 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 28 Apr 2016 16:44:25 +0900 Subject: [PATCH] ntdll: Add tests for buffer overflows in NtQueryDirectoryFile. Signed-off-by: Alexandre Julliard --- dlls/ntdll/directory.c | 20 +++--- dlls/ntdll/tests/directory.c | 160 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 157 insertions(+), 23 deletions(-) diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c index 4d431c08d30..41e7115ec51 100644 --- a/dlls/ntdll/directory.c +++ b/dlls/ntdll/directory.c @@ -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; } diff --git a/dlls/ntdll/tests/directory.c b/dlls/ntdll/tests/directory.c index f93c6234260..e801a09d3a2 100644 --- a/dlls/ntdll/tests/directory.c +++ b/dlls/ntdll/tests/directory.c @@ -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); -- 2.11.4.GIT