1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     StringUtil.cpp
8 
9 Abstract:
10 
11     This module implements string utlities in the framework
12 
13 Author:
14 
15 
16 
17 Environment:
18 
19     Both kernel and user mode
20 
21 Revision History:
22 
23 --*/
24 
25 #include "fxsupportpch.hpp"
26 
27 extern "C"  {
28 // #include "StringUtil.tmh"
29 }
30 
31 size_t
FxCalculateTotalStringSize(__in FxCollectionInternal * StringCollection,__in BOOLEAN Verify,__out_opt PBOOLEAN ContainsOnlyStrings)32 FxCalculateTotalStringSize(
33     __in FxCollectionInternal *StringCollection,
34     __in BOOLEAN Verify,
35     __out_opt PBOOLEAN ContainsOnlyStrings
36     )
37 {
38     size_t cbLength;
39     FxString *pString;
40     FxCollectionEntry* cur, *end;
41 
42     cbLength = 0;
43 
44     end = StringCollection->End();
45     for (cur = StringCollection->Start();
46          cur != end;
47          cur = cur->Next()) {
48         pString = (FxString *) cur->m_Object;
49 
50         if (Verify && pString->GetType() != FX_TYPE_STRING) {
51             *ContainsOnlyStrings = FALSE;
52             return 0;
53         }
54 
55         cbLength += pString->ByteLength(TRUE);
56     }
57 
58     if (ContainsOnlyStrings != NULL) {
59         *ContainsOnlyStrings = TRUE;
60     }
61 
62     if (StringCollection->Count() == 0) {
63         //
64         // If there are not entries, we still need 2 NULLs
65         //
66         cbLength = sizeof(UNICODE_NULL) * 2;
67     }
68     else {
69         //
70         // Extra NULL
71         //
72         cbLength += sizeof(UNICODE_NULL);
73     }
74 
75     //
76     // ASSERT that we are reporting an integral number of WCHARs in bytes
77     //
78     ASSERT((cbLength % sizeof(WCHAR)) == 0);
79 
80     return cbLength;
81 }
82 
83 size_t
FxCalculateTotalMultiSzStringSize(__in __nullnullterminated PCWSTR MultiSz)84 FxCalculateTotalMultiSzStringSize(
85     __in __nullnullterminated PCWSTR MultiSz
86     )
87 {
88     PCWSTR pCur;
89     size_t cbSize, cbLength;
90 
91     cbSize = 0;
92 
93     pCur = MultiSz;
94 
95     while (*pCur != NULL) {
96         //
97         // Compute length of string including terminating NULL
98         //
99         cbLength = (wcslen(pCur) + 1) * sizeof(WCHAR);
100 
101         cbSize += cbLength;
102         pCur = (PWSTR) WDF_PTR_ADD_OFFSET(pCur, cbLength);
103     }
104 
105     if (cbSize == 0) {
106         //
107         // If there are no strings, we still need 2 NULLs
108         //
109         cbSize = sizeof(UNICODE_NULL);
110     }
111 
112     //
113     // Final NULL which makes this a multi sz
114     //
115     cbSize += sizeof(UNICODE_NULL);
116 
117     //
118     // ASSERT that we are reporting an integral number of WCHARs in bytes
119     //
120     ASSERT((cbSize % sizeof(WCHAR)) == 0);
121 
122     return cbSize;
123 }
124 
125 #pragma prefast(push)
126 // Caller is responsible for allocating the correct amount of memory.
127 #pragma prefast(disable:__WARNING_INCORRECT_ANNOTATION_STRING )
128 PWSTR
FxCopyMultiSz(__out LPWSTR Buffer,__in FxCollectionInternal * StringCollection)129 FxCopyMultiSz(
130     __out LPWSTR Buffer,
131     __in FxCollectionInternal* StringCollection
132     )
133 {
134     LPWSTR pCur;
135     ULONG length;
136     FxCollectionEntry* cur, *end;
137 
138     pCur = Buffer;
139     end = StringCollection->End();
140 
141     for (cur = StringCollection->Start(); cur != end; cur = cur->Next()) {
142         FxString* pSourceString;
143 
144         pSourceString = (FxString *) cur->m_Object;
145 
146         length = pSourceString->ByteLength(TRUE);
147         RtlCopyMemory(pCur, pSourceString->Buffer(), length);
148 
149         //
150         // Length is expressed in number of bytes, not number of
151         // characters.
152         //
153         // length includes the NULL.
154         //
155         pCur = WDF_PTR_ADD_OFFSET_TYPE(pCur, length, LPWSTR);
156     }
157 
158     //
159     // If there are no entries, we still need 2 NULLs.
160     //
161     if (StringCollection->Count() == 0) {
162         *pCur = UNICODE_NULL;
163         pCur++;
164     }
165 
166     //
167     // double NULL terminate the string
168     //
169     *pCur = UNICODE_NULL;
170 
171     //
172     // Return the start of the next location in the buffer
173     //
174     return pCur + 1;
175 }
176 #pragma prefast(pop)
177 
178 _Must_inspect_result_
179 NTSTATUS
FxDuplicateUnicodeString(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in const UNICODE_STRING * Source,__out PUNICODE_STRING Destination)180 FxDuplicateUnicodeString(
181     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
182     __in const UNICODE_STRING* Source,
183     __out PUNICODE_STRING Destination
184     )
185 /*++
186 
187 Routine Description:
188     Makes a deep copy from Source to Destination.
189 
190     Destination is assumed to have been initialized by the caller, be owned by
191     an internal Fx object.  Destination could already contain a previously
192     allocated buffer.  If one exists and is large enough, it will be reused
193     for the copy.
194 
195     This function guarantees that the Buffer will be NULL terminated.  While
196     this is not necessary because Length describes the length of the buffer, the
197     resulting buffer can be copied back to the client driver and we cannot trust
198     the client driver to treat the string as an unterminated buffer, typically
199     the client driver will just extract the buffer and treat it as NULL
200     terminated.  To be defensive with this type of (mis)use, we always NULL
201     terminate the resuling Buffer.
202 
203 Arguments:
204     Source - source struct to copy from.  This string can originate from the
205              client driver.
206 
207     Destination - destination struct to copy to.  This struct is assumed to be
208                   internal and is not given to the outside caller
209 
210 Return Value:
211     NTSTATUS
212 
213   --*/
214 {
215     NTSTATUS status;
216     USHORT srcCbLength, srcCbLengthAndNull, dstMaxCbLength;
217 
218     //
219     // NOTE: We assume the sources string will be smaller than 64k.
220     //
221     srcCbLength = Source->Length;
222     dstMaxCbLength = Destination->MaximumLength;
223 
224     status = RtlUShortAdd(srcCbLength,
225                           sizeof(UNICODE_NULL),
226                           &srcCbLengthAndNull);
227     if (!NT_SUCCESS(status)) {
228         DoTraceLevelMessage(
229             FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
230             "Interger overflow occured when duplicating string %!STATUS!",
231             status);
232         return status;
233     }
234 
235     //
236     // First see if we already have enough memory to hold the string + a NULL
237     //
238     if (dstMaxCbLength < srcCbLengthAndNull) {
239         //
240         // Allocate enough memory for the source string and a NULL character
241         //
242         dstMaxCbLength = srcCbLengthAndNull;
243 
244         //
245         // We need to allocate memory.  Free any old memory first.
246         //
247         if (Destination->Buffer != NULL) {
248             FxPoolFree(Destination->Buffer);
249 
250             RtlZeroMemory(Destination, sizeof(UNICODE_STRING));
251         }
252 
253         Destination->Buffer = (PWSTR) FxPoolAllocate(
254             FxDriverGlobals, PagedPool, dstMaxCbLength);
255 
256         if (Destination->Buffer == NULL) {
257             status = STATUS_INSUFFICIENT_RESOURCES;
258             DoTraceLevelMessage(
259                 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGERROR,
260                 "Failed to allocate memory when duplicating string %!STATUS!",
261                 status);
262             return status;
263         }
264 
265         Destination->MaximumLength =  dstMaxCbLength;
266     }
267 
268     //
269     // If we get here and we have a buffer, then we can just copy the
270     // string into the buffer.
271     //
272     RtlCopyMemory(Destination->Buffer, Source->Buffer, srcCbLength);
273     Destination->Length = srcCbLength;
274 
275     //
276     // Make sure the string is NULL terminated and there is room for the NULL
277     //
278     ASSERT(Destination->Length + sizeof(UNICODE_NULL) <=
279                                                     Destination->MaximumLength);
280     Destination->Buffer[Destination->Length/sizeof(WCHAR)] = UNICODE_NULL;
281 
282     return STATUS_SUCCESS;
283 }
284 
285 _Must_inspect_result_
286 PWCHAR
FxDuplicateUnicodeStringToString(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in const UNICODE_STRING * Source)287 FxDuplicateUnicodeStringToString(
288     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
289     __in const UNICODE_STRING* Source
290     )
291 {
292     PWSTR pDuplicate;
293 
294     pDuplicate = (PWSTR) FxPoolAllocate(
295         FxDriverGlobals, PagedPool, Source->Length + sizeof(UNICODE_NULL));
296 
297     if (pDuplicate != NULL) {
298         RtlCopyMemory(pDuplicate, Source->Buffer, Source->Length);
299 
300         //
301         // Make sure the string is NULL terminated.  We can safely do this
302         // because we allocated an extra WCHAR for the null terminator.
303         //
304         pDuplicate[Source->Length/sizeof(WCHAR)] = UNICODE_NULL;
305     }
306 
307     return pDuplicate;
308 }
309