1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/files/memory_mapped_file.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <limits>
11 #include <string>
12
13 #include "base/files/file_path.h"
14 #include "base/logging.h"
15 #include "base/numerics/checked_math.h"
16 #include "base/threading/scoped_blocking_call.h"
17 #include "base/win/pe_image.h"
18
19 #include <windows.h>
20 #include <winnt.h> // NOLINT(build/include_order)
21
22 namespace base {
23
24 MemoryMappedFile::MemoryMappedFile() = default;
25
MapImageToMemory(Access access)26 bool MemoryMappedFile::MapImageToMemory(Access access) {
27 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
28
29 // The arguments to the calls of ::CreateFile(), ::CreateFileMapping(), and
30 // ::MapViewOfFile() need to be self consistent as far as access rights and
31 // type of mapping or one or more of them will fail in non-obvious ways.
32
33 if (!file_.IsValid())
34 return false;
35
36 file_mapping_.Set(::CreateFileMapping(file_.GetPlatformFile(), nullptr,
37 PAGE_READONLY | SEC_IMAGE_NO_EXECUTE, 0,
38 0, NULL));
39 if (!file_mapping_.is_valid())
40 return false;
41
42 data_ = static_cast<uint8_t*>(
43 ::MapViewOfFile(file_mapping_.get(), FILE_MAP_READ, 0, 0, 0));
44 if (!data_)
45 return false;
46
47 // We need to know how large the mapped file is in some cases
48
49 base::win::PEImage pe_image(data_);
50 length_ = pe_image.GetNTHeaders()->OptionalHeader.SizeOfImage;
51
52 return true;
53 }
54
MapFileRegionToMemory(const MemoryMappedFile::Region & region,Access access)55 bool MemoryMappedFile::MapFileRegionToMemory(
56 const MemoryMappedFile::Region& region,
57 Access access) {
58 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
59
60 DCHECK(access != READ_CODE_IMAGE || region == Region::kWholeFile);
61
62 if (!file_.IsValid())
63 return false;
64
65 DWORD view_access;
66 DWORD flags = 0;
67 ULARGE_INTEGER size = {};
68 switch (access) {
69 case READ_ONLY:
70 flags |= PAGE_READONLY;
71 view_access = FILE_MAP_READ;
72 break;
73 case READ_WRITE:
74 flags |= PAGE_READWRITE;
75 view_access = FILE_MAP_WRITE;
76 break;
77 case READ_WRITE_COPY:
78 flags |= PAGE_WRITECOPY;
79 view_access = FILE_MAP_COPY;
80 break;
81 case READ_WRITE_EXTEND:
82 flags |= PAGE_READWRITE;
83 view_access = FILE_MAP_WRITE;
84 size.QuadPart = region.size;
85 break;
86 case READ_CODE_IMAGE:
87 return MapImageToMemory(access);
88 }
89
90 file_mapping_.Set(::CreateFileMapping(file_.GetPlatformFile(), NULL, flags,
91 size.HighPart, size.LowPart, NULL));
92 if (!file_mapping_.is_valid())
93 return false;
94
95 ULARGE_INTEGER map_start = {};
96 SIZE_T map_size = 0;
97 int32_t data_offset = 0;
98
99 if (region == MemoryMappedFile::Region::kWholeFile) {
100 DCHECK_NE(READ_WRITE_EXTEND, access);
101 int64_t file_len = file_.GetLength();
102 if (file_len <= 0 || !IsValueInRangeForNumericType<size_t>(file_len))
103 return false;
104 length_ = static_cast<size_t>(file_len);
105 } else {
106 // The region can be arbitrarily aligned. MapViewOfFile, instead, requires
107 // that the start address is aligned to the VM granularity (which is
108 // typically larger than a page size, for instance 32k).
109 // Also, conversely to POSIX's mmap, the |map_size| doesn't have to be
110 // aligned and must be less than or equal the mapped file size.
111 // We map here the outer region [|aligned_start|, |aligned_start+size|]
112 // which contains |region| and then add up the |data_offset| displacement.
113 int64_t aligned_start = 0;
114 size_t ignored = 0U;
115 CalculateVMAlignedBoundaries(region.offset, region.size, &aligned_start,
116 &ignored, &data_offset);
117 base::CheckedNumeric<SIZE_T> full_map_size = region.size;
118 full_map_size += data_offset;
119
120 // Ensure that the casts below in the MapViewOfFile call are sane.
121 if (aligned_start < 0 || !full_map_size.IsValid()) {
122 DLOG(ERROR) << "Region bounds are not valid for MapViewOfFile";
123 return false;
124 }
125 map_start.QuadPart = static_cast<uint64_t>(aligned_start);
126 map_size = full_map_size.ValueOrDie();
127 length_ = region.size;
128 }
129
130 data_ = static_cast<uint8_t*>(::MapViewOfFile(file_mapping_.get(),
131 view_access, map_start.HighPart,
132 map_start.LowPart, map_size));
133 if (data_ == nullptr)
134 return false;
135 data_ += data_offset;
136 return true;
137 }
138
CloseHandles()139 void MemoryMappedFile::CloseHandles() {
140 if (data_)
141 ::UnmapViewOfFile(data_);
142 if (file_mapping_.is_valid())
143 file_mapping_.Close();
144 if (file_.IsValid())
145 file_.Close();
146
147 data_ = nullptr;
148 length_ = 0;
149 }
150
151 } // namespace base
152