summaryrefslogtreecommitdiff
path: root/src/classlibnative/nls/nlstable.cpp
blob: dd395153d4eb06896d24c619947deace5626968c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#include "common.h"
#include <winwrap.h>
#include <excep.h>          // For COMPlusThrow
#include <appdomain.hpp>
#include <assembly.hpp>
#include "nlstable.h"       // Class declaration

#if FEATURE_CODEPAGES_FILE

/*=================================CreateSharedMemoryMapping==================================
**Action: Create a file mapping object which can be shared among different users under Windows NT/2000.
**        Actually its just a memory mapped section of the swapfile.
**Returns: The file mapping handle.  NULL if any error happens.
**Arguments:
**      pMappingName    the name of the file mapping object.
**      iSize           Size to use
**Exceptions: 
**Note:
**      This function creates a DACL which grants GENERIC_ALL access to members of the "Everyone" group.
**      Then create a security descriptor using this DACL.  Finally, use this SA to create the file mapping object.
** WARNING:
**      This creates a shared file or shared paged memory section (if hFile == INVALID_HANDLE_VALUE) that is shared machine-wide
**      Therefore for side-by-side to work, the mapping names must be unique per version!
**      We utilize this feature for code pages in case it hasn't changed across versions we can still reuse the
**      tables, but it seems suspicious for other applications (as commented in MapDataFile below)
==============================================================================*/
// static method
HANDLE NLSTable::CreateSharedMemoryMapping(const LPCWSTR pMappingName, const int iSize ) {
    CONTRACTL {
        THROWS;
        GC_NOTRIGGER;
        MODE_ANY;
        PRECONDITION(iSize > 0);
        PRECONDITION(CheckPointer(pMappingName));
    } CONTRACTL_END;
    
    HANDLE hFileMap = NULL;

    SECURITY_DESCRIPTOR sd ;
    SECURITY_ATTRIBUTES sa ; 

    //
    // Create the sid for the Everyone group.
    //
    SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY;
    PSID pSID = NULL;     
    int nSidSize;
    
    PACL pDACL = NULL; 
    int nAclSize;

    CQuickBytes newBuffer;
    
    if (!AllocateAndInitializeSid(&siaWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pSID)) {            
        goto ErrorExit;
    }

    nSidSize = GetLengthSid(pSID);

    //
    // Create Discretionary Access Control List (DACL).
    //
    
    // First calculate the size of the DACL, since this is a linked-list like structure which contains one or more 
    // ACE (access control entry)    
    nAclSize = sizeof(ACL)                          // the header structure of ACL
        + sizeof(ACCESS_ALLOWED_ACE) + nSidSize;     // and one "access allowed ACE".

    // We know the size needed for DACL now, so create it.        
    // An exception is thrown if OOM happens.
    pDACL = (PACL) (newBuffer.AllocThrows(nAclSize));
    if(!InitializeAcl( pDACL, nAclSize, ACL_REVISION ))
        goto ErrorExit;  

    // Add the "access allowed ACE", meaning:
    //    we will allow members of the "Everyone" group to have SECTION_MAP_READ | SECTION_QUERY access to the file mapping object.
    //    for memory sections the creator will still be allowed to create it.
    if(!AddAccessAllowedAce( pDACL, ACL_REVISION, SECTION_MAP_READ | SECTION_QUERY, pSID ))
        goto ErrorExit; 

    //
    // Create Security descriptor (SD).
    //
    if(!InitializeSecurityDescriptor( &sd, SECURITY_DESCRIPTOR_REVISION ))
        goto ErrorExit; 
    // Set the previously created DACL to this SD.
    if(!SetSecurityDescriptorDacl( &sd, TRUE, pDACL, FALSE ))
        goto ErrorExit; 

    // Create Security Attribute (SA).        
    sa.nLength = sizeof( sa ) ;
    sa.bInheritHandle = TRUE ; 
    sa.lpSecurityDescriptor = &sd ;

    //
    // Finally, create the file mapping using the SA.
    //

    // If we are on Windows 2000 or later, try to open it in global namespace.  The \global namespace is ignored if
    // Terminal service is not running.
    WCHAR globalSectionName[MAX_LONGPATH];
    wcscpy_s(globalSectionName, COUNTOF(globalSectionName), W("Global\\"));
    if (wcslen(pMappingName) + wcslen(globalSectionName) >= MAX_LONGPATH) {
        goto ErrorExit;            
    }
    wcscat_s(globalSectionName, COUNTOF(globalSectionName), pMappingName);
    
    // Try to create the section in the Global\ namespace.  The CreateFileMapping() will ignore Global\ namespace if Terminal Service
    // is not running.
    hFileMap = WszCreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, iSize, globalSectionName);
    
    // If not allowed to be global (like terminal server) or not WinNT, then open it in local namespace.
    if (hFileMap == NULL)
    {
        // Not WinNT or access denied for Global\ namespace, try the local namespace.  When Terminal service is running, the Local\ namespace
        // means the namespace "Sessions\<n>\BasedNamedObjects".
        hFileMap = WszCreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, iSize, pMappingName);
    }        
    
ErrorExit:    
    if(pSID)
        FreeSid( pSID );

    // If still not allowed, try building one with no name
    if (hFileMap == NULL)
    {
        hFileMap = WszCreateFileMapping( 
            INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, iSize, NULL);    
    }

    return (hFileMap) ;
}

/*=================================OpenOrCreateMemoryMapping==================================
**Action: Opens an existing memory mapped object, or creates a new one (by calling above fn).
**        Worst case just allocate some memory.
**Returns: The pointer to our memory.  NULL if any error happens.
**Arguments:
**      pMappingName    the name of the file mapping object.
**      iSize           Size to use
**Exceptions:
**
**IMPORTANT:
**      Memory mapped sections are cleared when set.  We expect the caller to set the last int
**      to a non-zero value, so we test this flag.  If it is still zero when we open it, we
**      assume that we've gotten a result in an unfinished state and allocate a new one instead
**      of trying to use the one with the zeros.
**
**Note:
**      This function creates a DACL which grants GENERIC_ALL access to members of the "Everyone" group.
**      Then create a security descriptor using this DACL.  Finally, use this SA to create the file mapping object.
** WARNING:
**      This creates a shared file or shared paged memory section (if hFile == INVALID_HANDLE_VALUE) that is shared machine-wide
**      Therefore for side-by-side to work, the mapping names must be unique per version!
**      We utilize this feature for code pages in case it hasn't changed across versions we can still reuse the
**      tables, but it seems suspicious for other applications (as commented in MapDataFile below)
==============================================================================*/
PBYTE NLSTable::OpenOrCreateMemoryMapping(const LPCWSTR pMappingName, const int iSize, HANDLE* mappedFile)
{
    CONTRACTL
    {
        THROWS;
        DISABLED(GC_TRIGGERS); // 
        MODE_ANY;
        PRECONDITION(iSize % 4 == 0);
        PRECONDITION(iSize > 0);
        PRECONDITION(CheckPointer(pMappingName));
        PRECONDITION(CheckPointer(mappedFile));
    } CONTRACTL_END;

    _ASSERTE(pMappingName != NULL); // Must have a string name.
    _ASSERTE(iSize > 0);            // Pointless to have <= 0 allocation
    _ASSERTE(iSize % 4 == 0);       // Need 4 byte alignment for flag check

    LPVOID  pResult = NULL;

    // Try creating/opening it.
    HANDLE  hMap = NULL;

    *mappedFile = hMap;
    // Calls into OS a lot, should switch to preemp mode
    GCX_PREEMP();

    // First try opening it.  It might already be in existence
    // The assumption here is that it's rare that we will hit the cases where two or more threads are trying to create
    // the named section at the same time.  Therefore, the following code does not use critical section or mutex trying
    // to synchornize different threads.

    // Try to open it in global namespace. The global\ namespace is ignored if terminal service is not running.
    WCHAR globalSectionName[MAX_LONGPATH];
    wcscpy_s(globalSectionName, COUNTOF(globalSectionName), W("Global\\"));
    if (wcslen(pMappingName) + wcslen(globalSectionName) >= MAX_LONGPATH)
        return NULL;
    
    wcscat_s(globalSectionName, COUNTOF(globalSectionName), pMappingName);
    
    hMap = WszOpenFileMapping(FILE_MAP_READ, TRUE, globalSectionName);
    if (hMap == NULL) {
        // If access is denied for global\namespace or the name is not opened in global namespace, try the local namespace.
        // Also if we're rotor or win 9x.
        hMap = WszOpenFileMapping(FILE_MAP_READ, TRUE, pMappingName);       
    }  
    
    if (hMap != NULL) {
        // We got a section, map a view, READ ONLY!
        pResult = MapViewOfFile( hMap, FILE_MAP_READ, 0, 0, 0);

        // Anything found?
        if (pResult != NULL)
        {
            // Make sure our result is allocated.  We expect a non-0 flag to be set for last int of our section
            const int* pFlag = (int*)(((BYTE*)pResult) + iSize - 4);
            if (*pFlag != 0)
            {
                *mappedFile = hMap;
                // Found a valid already opened section!
                return (PBYTE)pResult;
            }

            // Couldn't find it, unmap it.
            UnmapViewOfFile(pResult);
            pResult = NULL;
        }

        // We can't use this one, so close it
        CloseHandle(hMap);
        hMap = NULL;
    }
    
    // Didn't get a section, try to create one, NT/XP/.Net gets security permissions, 9X doesn't,
    // but our helper fn takes care of that for us.
    hMap = NLSTable::CreateSharedMemoryMapping(pMappingName, iSize);

    // Were we successfull?
    if (hMap != NULL)
    {
        // We have hMap, try to get our section
        pResult = MapViewOfFile( hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);

        // Don't close hMap unless we aren't using it.
        // That confuses the mapping stuff and we lose the name, it'll close when runtime shuts down.
        if (pResult == NULL)
        {
            CloseHandle(hMap);
            hMap = NULL;
        }
        // There is no need to zero out the mapCodePageCached field, since the initial contents of the pages in the file mapping object are zero.
        
        *mappedFile = hMap;
    }

    return (PBYTE)pResult;
}

#endif