From b7607aebdce235b486eee020a06e993e8ee6c661 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 3 Jan 2006 17:34:43 +0100 Subject: [PATCH] ntdll: Correct implementation for atom query functions. --- dlls/ntdll/atom.c | 76 +++++++++++++++++++++++----------- dlls/ntdll/tests/atom.c | 57 +++++++++++++++++++++++++ include/wine/server_protocol.h | 3 +- server/atom.c | 5 ++- server/protocol.def | 1 + server/trace.c | 1 + 6 files changed, 116 insertions(+), 27 deletions(-) diff --git a/dlls/ntdll/atom.c b/dlls/ntdll/atom.c index bdef98b2d1e..9ea72c5c9e7 100644 --- a/dlls/ntdll/atom.c +++ b/dlls/ntdll/atom.c @@ -97,6 +97,25 @@ NTSTATUS WINAPI RtlDeleteAtomFromAtomTable( RTL_ATOM_TABLE table, RTL_ATOM atom return status; } +/****************************************************************** + * integral_atom_name (internal) + * + * Helper for fetching integral (local/global) atoms names. + */ +static ULONG integral_atom_name(WCHAR* buffer, ULONG len, RTL_ATOM atom) +{ + static WCHAR fmt[] = {'#','%','u',0}; + WCHAR tmp[16]; + int ret; + + ret = sprintfW( tmp, fmt, atom ); + if (!len) return ret * sizeof(WCHAR); + if (len <= ret) ret = len - 1; + memcpy( buffer, tmp, ret * sizeof(WCHAR) ); + buffer[ret] = 0; + return ret * sizeof(WCHAR); +} + /****************************************************************** * RtlQueryAtomInAtomTable (NTDLL.@) */ @@ -104,18 +123,13 @@ NTSTATUS WINAPI RtlQueryAtomInAtomTable( RTL_ATOM_TABLE table, RTL_ATOM atom, UL ULONG* pin, WCHAR* name, ULONG* len ) { NTSTATUS status = STATUS_SUCCESS; - WCHAR full_name[MAX_ATOM_LEN]; DWORD wlen = 0; if (!table) status = STATUS_INVALID_PARAMETER; else if (atom < MAXINTATOM) { if (!atom) return STATUS_INVALID_PARAMETER; - if (len) - { - static WCHAR fmt[] = {'#','%','d',0}; - wlen = sprintfW( full_name, fmt, atom ) * sizeof(WCHAR); - } + if (len) wlen = integral_atom_name( name, *len, atom); if (ref) *ref = 1; if (pin) *pin = 1; } @@ -125,11 +139,12 @@ NTSTATUS WINAPI RtlQueryAtomInAtomTable( RTL_ATOM_TABLE table, RTL_ATOM atom, UL { req->atom = atom; req->table = table; - if (len) wine_server_set_reply( req, full_name, sizeof(full_name) ); + if (len && *len && name) + wine_server_set_reply( req, name, *len ); status = wine_server_call( req ); if (status == STATUS_SUCCESS) { - wlen = wine_server_reply_size( reply ); + wlen = reply->total; if (ref) *ref = reply->count; if (pin) *pin = reply->pinned; } @@ -138,17 +153,17 @@ NTSTATUS WINAPI RtlQueryAtomInAtomTable( RTL_ATOM_TABLE table, RTL_ATOM atom, UL } if (status == STATUS_SUCCESS && len) { - if (*len > wlen) + if (*len) { - memcpy( name, full_name, wlen ); - name[wlen / sizeof(WCHAR)] = 0; + wlen = min( *len - sizeof(WCHAR), wlen ); + if (name) name[wlen / sizeof(WCHAR)] = 0; } else status = STATUS_BUFFER_TOO_SMALL; *len = wlen; } - TRACE( "%p %x -> %s (%lu)\n", - table, atom, len ? debugstr_w(name) : NULL, status ); + TRACE( "%p %x -> %s (%lx)\n", + table, atom, len ? debugstr_wn(name, wlen / sizeof(WCHAR)) : NULL, status ); return status; } @@ -372,20 +387,20 @@ NTSTATUS WINAPI NtQueryInformationAtom( RTL_ATOM atom, ATOM_INFORMATION_CLASS cl ULONG name_len; ATOM_BASIC_INFORMATION* abi = (ATOM_BASIC_INFORMATION*)ptr; - name_len = size - (sizeof(ATOM_BASIC_INFORMATION) - sizeof(WCHAR)); + if (size < sizeof(ATOM_BASIC_INFORMATION)) + return STATUS_INVALID_PARAMETER; + name_len = size - sizeof(ATOM_BASIC_INFORMATION); if (atom < MAXINTATOM) { - if (!atom) status = STATUS_INVALID_PARAMETER; - else if (name_len >= 7 * sizeof(WCHAR)) + if (atom) { - static WCHAR fmt[] = {'#','%','d',0}; - abi->NameLength = snprintfW( abi->Name, name_len / sizeof(WCHAR), fmt, atom ) * sizeof(WCHAR); + abi->NameLength = integral_atom_name( abi->Name, name_len, atom ); + status = (name_len) ? STATUS_SUCCESS : STATUS_BUFFER_TOO_SMALL; abi->ReferenceCount = 1; abi->Pinned = 1; - status = STATUS_SUCCESS; } - else status = STATUS_BUFFER_TOO_SMALL; + else status = STATUS_INVALID_PARAMETER; } else { @@ -395,19 +410,32 @@ NTSTATUS WINAPI NtQueryInformationAtom( RTL_ATOM atom, ATOM_INFORMATION_CLASS cl req->table = NULL; if (name_len) wine_server_set_reply( req, abi->Name, name_len ); status = wine_server_call( req ); - name_len = wine_server_reply_size( reply ); if (status == STATUS_SUCCESS) { - abi->NameLength = name_len; + name_len = wine_server_reply_size( reply ); + if (name_len) + { + abi->NameLength = name_len; + abi->Name[name_len / sizeof(WCHAR)] = '\0'; + } + else + { + name_len = reply->total; + abi->NameLength = name_len; + status = STATUS_BUFFER_TOO_SMALL; + } abi->ReferenceCount = reply->count; abi->Pinned = reply->pinned; } + else name_len = 0; } SERVER_END_REQ; } - TRACE( "%x -> %s (%lu)\n", atom, debugstr_wn(abi->Name, name_len/sizeof(WCHAR)), status ); + TRACE( "%x -> %s (%lu)\n", + atom, debugstr_wn(abi->Name, abi->NameLength / sizeof(WCHAR)), + status ); if (psize) - *psize = sizeof(ATOM_BASIC_INFORMATION) - sizeof(WCHAR) + name_len; + *psize = sizeof(ATOM_BASIC_INFORMATION) + name_len; } break; default: diff --git a/dlls/ntdll/tests/atom.c b/dlls/ntdll/tests/atom.c index c812ed7a8cf..a48e2cf473f 100644 --- a/dlls/ntdll/tests/atom.c +++ b/dlls/ntdll/tests/atom.c @@ -54,6 +54,9 @@ static NTSTATUS (WINAPI *pRtlLookupAtomInAtomTable)(RTL_ATOM_TABLE,PCWSTR,PRTL_A static NTSTATUS (WINAPI *pRtlPinAtomInAtomTable)(RTL_ATOM_TABLE,RTL_ATOM); static NTSTATUS (WINAPI *pRtlQueryAtomInAtomTable)(RTL_ATOM_TABLE,RTL_ATOM,PULONG,PULONG,PWSTR,PULONG); +static NTSTATUS (WINAPI* pNtAddAtom)(LPCWSTR,ULONG,RTL_ATOM*); +static NTSTATUS (WINAPI* pNtQueryInformationAtom)(RTL_ATOM,DWORD,void*,ULONG,PULONG); + static const WCHAR EmptyAtom[] = {0}; static const WCHAR testAtom1[] = {'H','e','l','l','o',' ','W','o','r','l','d',0}; static const WCHAR testAtom2[] = {'H','e','l','l','o',' ','W','o','r','l','d','2',0}; @@ -81,6 +84,9 @@ static void InitFunctionPtr(void) pRtlLookupAtomInAtomTable = (void *)GetProcAddress(hntdll, "RtlLookupAtomInAtomTable"); pRtlPinAtomInAtomTable = (void *)GetProcAddress(hntdll, "RtlPinAtomInAtomTable"); pRtlQueryAtomInAtomTable = (void *)GetProcAddress(hntdll, "RtlQueryAtomInAtomTable"); + + pNtAddAtom = (void *)GetProcAddress(hntdll, "NtAddAtom"); + pNtQueryInformationAtom = (void *)GetProcAddress(hntdll, "NtQueryInformationAtom"); } } @@ -192,6 +198,15 @@ static void test_NtAtom(void) ok(!strcmpW(Name, testAtom2), "We found wrong atom\n"); ok((strlenW(testAtom2) * sizeof(WCHAR)) == Len, "Returned wrong length %ld\n", Len); + Len = 8; + Name[0] = Name[1] = Name[2] = Name[3] = Name[4] = 0x55AA; + res = pRtlQueryAtomInAtomTable(AtomTable, Atom2, NULL, NULL, Name, &Len); + ok(!res, "query atom %lx\n", res); + ok(Len == 6, "wrong length %lu\n", Len); + ok(!memcmp(Name, testAtom2, Len), "wrong atom string\n"); + ok(!Name[3], "wrong string termination\n"); + ok(Name[4] == 0x55AA, "buffer overwrite\n"); + res = pRtlLookupAtomInAtomTable(AtomTable, testAtom2, &testAtom); ok(!res, "We can't find our pinned atom!! retval: %lx\n", res); ok(testAtom == Atom2, "We found wrong atom!!!\n"); @@ -402,6 +417,47 @@ static void test_NtRefPinAtom(void) } } +static void test_Global(void) +{ + NTSTATUS res; + RTL_ATOM atom; + char ptr[sizeof(ATOM_BASIC_INFORMATION) + 255 * sizeof(WCHAR)]; + ATOM_BASIC_INFORMATION* abi = (ATOM_BASIC_INFORMATION*)ptr; + ULONG ptr_size = sizeof(ATOM_BASIC_INFORMATION) + 255 * sizeof(WCHAR); + + res = pNtAddAtom(testAtom1, lstrlenW(testAtom1) * sizeof(WCHAR), &atom); + ok(!res, "Added atom (%lx)\n", res); + + memset(abi->Name, 0x55, 255 * sizeof(WCHAR)); + res = pNtQueryInformationAtom( atom, AtomBasicInformation, (void*)ptr, ptr_size, NULL ); + ok(!res, "atom lookup\n"); + ok(!lstrcmpW(abi->Name, testAtom1), "ok strings\n"); + ok(abi->NameLength == lstrlenW(testAtom1) * sizeof(WCHAR), "wrong string length\n"); + ok(abi->Name[lstrlenW(testAtom1)] == 0, "wrong string termination %x\n", abi->Name[lstrlenW(testAtom1)]); + ok(abi->Name[lstrlenW(testAtom1) + 1] == 0x5555, "buffer overwrite %x\n", abi->Name[lstrlenW(testAtom1) + 1]); + + ptr_size = sizeof(ATOM_BASIC_INFORMATION); + res = pNtQueryInformationAtom( atom, AtomBasicInformation, (void*)ptr, ptr_size, NULL ); + ok(res == STATUS_BUFFER_TOO_SMALL, "wrong return status (%lx)\n", res); + ok(abi->NameLength == lstrlenW(testAtom1) * sizeof(WCHAR), "ok string length\n"); + + memset(abi->Name, 0x55, lstrlenW(testAtom1) * sizeof(WCHAR)); + ptr_size = sizeof(ATOM_BASIC_INFORMATION) + lstrlenW(testAtom1) * sizeof(WCHAR); + res = pNtQueryInformationAtom( atom, AtomBasicInformation, (void*)ptr, ptr_size, NULL ); + ok(!res, "atom lookup %lx\n", res); + ok(!lstrcmpW(abi->Name, testAtom1), "strings don't match\n"); + ok(abi->NameLength == lstrlenW(testAtom1) * sizeof(WCHAR), "wrong string length\n"); + ok(abi->Name[lstrlenW(testAtom1)] == 0, "buffer overwrite %x\n", abi->Name[lstrlenW(testAtom1)]); + ok(abi->Name[lstrlenW(testAtom1) + 1] == 0x5555, "buffer overwrite %x\n", abi->Name[lstrlenW(testAtom1) + 1]); + + ptr_size = sizeof(ATOM_BASIC_INFORMATION) + 4 * sizeof(WCHAR); + abi->Name[0] = abi->Name[1] = abi->Name[2] = abi->Name[3] = '\0'; + res = pNtQueryInformationAtom( atom, AtomBasicInformation, (void*)ptr, ptr_size, NULL ); + ok(!res, "couldn't find atom\n"); + ok(abi->NameLength == 8, "wrong string length %u\n", abi->NameLength); + ok(!memcmp(abi->Name, testAtom1, 8), "strings don't match\n"); +} + START_TEST(atom) { InitFunctionPtr(); @@ -410,5 +466,6 @@ START_TEST(atom) test_NtAtom(); test_NtIntAtom(); test_NtRefPinAtom(); + test_Global(); } } diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index c34fd464760..ec12eae6a39 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -2078,6 +2078,7 @@ struct get_atom_information_reply struct reply_header __header; int count; int pinned; + size_t total; /* VARARG(name,unicode_str); */ }; @@ -4346,6 +4347,6 @@ union generic_reply struct query_symlink_reply query_symlink_reply; }; -#define SERVER_PROTOCOL_VERSION 217 +#define SERVER_PROTOCOL_VERSION 218 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/atom.c b/server/atom.c index bf5851eff67..18a59dbffd7 100644 --- a/server/atom.c +++ b/server/atom.c @@ -413,10 +413,11 @@ DECL_HANDLER(get_atom_information) if ((entry = get_atom_entry( table, req->atom ))) { size_t len = entry->len * sizeof(WCHAR); - if (len <= get_reply_max_size()) set_reply_data( entry->str, len ); - else if (get_reply_max_size()) set_error( STATUS_BUFFER_OVERFLOW ); + if (get_reply_max_size()) + set_reply_data( entry->str, min( len, get_reply_max_size())); reply->count = entry->count; reply->pinned = entry->pinned; + reply->total = len; } else reply->count = -1; release_object( table ); diff --git a/server/protocol.def b/server/protocol.def index 39851b25466..65d18b9e18d 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1488,6 +1488,7 @@ enum char_info_mode @REPLY int count; /* atom lock count */ int pinned; /* whether the atom has been pinned */ + size_t total; /* actual length of atom name */ VARARG(name,unicode_str); /* atom name */ @END diff --git a/server/trace.c b/server/trace.c index 409d9de60bf..cb9fe822b76 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1961,6 +1961,7 @@ static void dump_get_atom_information_reply( const struct get_atom_information_r { fprintf( stderr, " count=%d,", req->count ); fprintf( stderr, " pinned=%d,", req->pinned ); + fprintf( stderr, " total=%d,", req->total ); fprintf( stderr, " name=" ); dump_varargs_unicode_str( cur_size ); }