Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(361)

Side by Side Diff: syzygy/runlaa/runlaa_app.cc

Issue 1269553002: Create a utility for running executables in LAA mode. (Closed) Base URL: https://github.com/google/syzygy.git@master
Patch Set: Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "syzygy/runlaa/runlaa_app.h"
16
17 #include "base/path_service.h"
18 #include "base/files/file_util.h"
19 #include "base/files/scoped_temp_dir.h"
20 #include "base/process/launch.h"
21 #include "base/win/pe_image.h"
22 #include "syzygy/core/file_util.h"
23 #include "syzygy/pe/pe_file.h"
24
25 namespace runlaa {
26
27 namespace {
28
29 static const char kUsageFormatStr[] =
30 "Usage: %ls [options] -- [options for child process]\n"
31 "Required Options:\n"
32 " --image=<FILE> Path of the image to run."
33 " --mode=<MODE> Runs the provided executable with the given mode.\n"
34 " MODE must be one of 'laa' or 'nolaa'.\n"
35 "Optional Options:\n"
36 " --expect-mode=<MODE>\n"
37 " If specified then returns 0 if the currently running\n"
38 " mode matches the expected mode, 1 otherwise. This is\n"
39 " to allow self-unittesting.\n"
40 " --in-place Modifies the image in-place if necessary. Returns the\n"
41 " image to its original state when completed.\n"
42 " --keep-temp-dir If specified then the temp directory will not be\n"
43 " deleted.\n"
44 "\n";
45
46 static const char kInPlace[] = "in-place";
47 static const char kImage[] = "image";
48 static const char kKeepTempDir[] = "keep-temp-dir";
49 static const char kMode[] = "mode";
50 static const char kModeLaa[] = "laa";
51 static const char kModeNoLaa[] = "nolaa";
52
53 // Gets the status of the LargeAddressAware bit for the given image.
54 bool GetLaaBit(const base::FilePath& image_path, bool* is_laa) {
55 DCHECK_NE(static_cast<bool*>(nullptr), is_laa);
56
57 if (!base::PathExists(image_path)) {
58 LOG(ERROR) << "Image does not exist: " << image_path.value();
59 return false;
60 }
61
62 pe::PEFile image;
63 if (!image.Init(image_path)) {
64 LOG(ERROR) << "Unable to open PE file: " << image_path.value();
65 return false;
66 }
67
68 *is_laa = (image.nt_headers()->FileHeader.Characteristics &
69 IMAGE_FILE_LARGE_ADDRESS_AWARE) != 0;
70
71 return true;
72 }
73
74 // Toggles the LargeAddressAware bit for the given image.
75 bool ToggleLaaBit(const base::FilePath& image_path) {
76 base::ScopedFILE file(base::OpenFile(image_path, "r+b"));
77 if (file.get() == nullptr) {
78 LOG(ERROR) << "Unable to open for reading and writing: "
79 << image_path.value();
80 return false;
81 }
82
83 // Read the DOS header.
84 IMAGE_DOS_HEADER dos_header = {};
85 if (fread(&dos_header, sizeof(dos_header), 1, file.get()) != 1) {
86 LOG(ERROR) << "Unable to read DOS header:" << image_path.value();
87 return false;
88 }
89
90 // Get the offset of the image characteristics.
91 size_t characteristics_offset = dos_header.e_lfanew +
92 offsetof(IMAGE_NT_HEADERS, FileHeader) +
93 offsetof(IMAGE_FILE_HEADER, Characteristics);
94 WORD characteristics = 0;
95 if (::fseek(file.get(), characteristics_offset, SEEK_SET) != 0 ||
96 ::fread(&characteristics, sizeof(characteristics), 1, file.get()) != 1) {
97 LOG(ERROR) << "Unable to read image characteristics: "
98 << image_path.value();
99 return false;
100 }
101
102 // Toggle the bit and write it back to the image.
103 characteristics ^= IMAGE_FILE_LARGE_ADDRESS_AWARE;
104 if (::fseek(file.get(), characteristics_offset, SEEK_SET) != 0 ||
105 ::fwrite(&characteristics, sizeof(characteristics), 1, file.get()) != 1) {
106 LOG(ERROR) << "Unable to write image characteristics: "
107 << image_path.value();
108 return false;
109 }
110
111 return true;
112 }
113
114 bool CurrentProcessIsLargeAddressAware() {
115 const base::win::PEImage image(::GetModuleHandle(NULL));
116
117 bool process_is_large_address_aware =
118 (image.GetNTHeaders()->FileHeader.Characteristics &
119 IMAGE_FILE_LARGE_ADDRESS_AWARE) != 0;
120
121 return process_is_large_address_aware;
122 }
123
124 bool SelfTest(const std::string& expect_mode) {
125 bool is_laa = CurrentProcessIsLargeAddressAware();
126 if (expect_mode == kModeLaa)
127 return is_laa;
128 if (expect_mode == kModeNoLaa)
129 return !is_laa;
130 return false;
131 }
132
133 } // namespace
134
135 bool RunLaaApp::ParseCommandLine(const base::CommandLine* command_line) {
136 DCHECK_NE(static_cast<const base::CommandLine*>(nullptr), command_line);
137
138 if (command_line->HasSwitch("help")) {
139 ::fprintf(err(), kUsageFormatStr,
140 command_line->GetProgram().BaseName().value().c_str());
141 return false;
142 }
143
144 // If the executable is running a self-hosted test, then don't bother parsing
145 // anything else.
146 expect_mode_ = command_line->GetSwitchValueASCII("expect-mode");
147 if (!expect_mode_.empty())
148 return true;
149
150 // Parse the image.
151 if (!command_line->HasSwitch(kImage)) {
152 LOG(ERROR) << "Must specify --" << kImage << ".";
153 return false;
154 }
155 image_ = base::MakeAbsoluteFilePath(command_line->GetSwitchValuePath(kImage));
156
157 // Parse the mode.
158 if (!command_line->HasSwitch(kMode)) {
159 LOG(ERROR) << "Must specify --" << kMode << ".";
160 return false;
161 }
162 std::string mode;
163 mode = command_line->GetSwitchValueASCII(kMode);
164 if (mode == kModeLaa) {
165 is_laa_ = true;
166 } else if (mode == kModeNoLaa) {
167 is_laa_ = false;
168 } else {
169 LOG(ERROR) << "Unrecognized mode: " << mode;
170 return false;
171 }
172
173 // Parse optional options.
174 in_place_ = command_line->HasSwitch(kInPlace);
175 keep_temp_dir_ = command_line->HasSwitch(kKeepTempDir);
176
177 // Copy the child process arguments.
178 child_argv_ = command_line->GetArgs();
179
180 return true;
181 }
182
183 int RunLaaApp::Run() {
184 // If an expected mode has been specified then run a self-test and return
185 // the result.
186 if (!expect_mode_.empty()) {
187 if (SelfTest(expect_mode_))
188 return 0;
189 return 1;
190 }
191
192 bool is_laa = false;
Sigurður Ásgeirsson 2015/08/03 10:27:28 nit: was_laa to make subsequent code more readable
chrisha 2015/08/03 13:13:46 Done.
193 if (!GetLaaBit(image_, &is_laa))
194 return 1;
195
196 base::ScopedTempDir scoped_temp_dir;
197 base::FilePath child_image(image_);
198 bool toggle_back = false;
199
200 if (is_laa == is_laa_) {
201 LOG(INFO) << "Image already in desired mode, running directly.";
202 } else {
203 // The image is not in the desired mode. It needs to be toggled.
204 if (in_place_) {
205 // Try our best not to modify the currently running executable.
206 base::FilePath exe_path;
207 if (PathService::Get(base::FILE_EXE, &exe_path)) {
208 exe_path = base::MakeAbsoluteFilePath(exe_path);
209 core::FilePathCompareResult result =
210 core::CompareFilePaths(exe_path, image_);
211 if (result == core::kEquivalentFilePaths) {
212 LOG(ERROR) << "Unable to modify running executable in-place.";
213 return 1;
214 }
215 }
216
217 // The work is occurring in place and the image needs to be toggled back.
218 toggle_back = true;
219 } else {
220 // The work is not to happen in place. Create a temp directory and copy
221 // the
Sigurður Ásgeirsson 2015/08/03 10:27:28 nit: yuck!
chrisha 2015/08/03 13:13:46 Done.
222 // image.
223 if (!scoped_temp_dir.CreateUniqueTempDir()) {
224 LOG(ERROR) << "Failed to create temp directory.";
225 return 1;
226 }
227
228 // Take ownership of the temp directory if it is to be left around.
229 base::FilePath temp_dir = scoped_temp_dir.path();
230 if (keep_temp_dir_) {
231 temp_dir = scoped_temp_dir.Take();
232 LOG(INFO) << "Temporary directory will be preserved: "
233 << temp_dir.value();
234 }
235
236 child_image = temp_dir.Append(image_.BaseName());
237 LOG(INFO) << "Creating copy of image: " << child_image.value();
238 if (!base::CopyFile(image_, child_image)) {
239 LOG(ERROR) << "Failed to copy image.";
240 return 1;
241 }
242 }
243
244 // Toggle the image.
245 LOG(INFO) << "Toggling LargeAddressAware bit: " << child_image.value();
246 if (!ToggleLaaBit(child_image))
247 return 1;
248 }
249
250 // Run the child process.
251 base::CommandLine::StringVector child_argv(child_argv_);
252 child_argv.insert(child_argv.begin(), child_image.value());
253 base::CommandLine child_command_line(child_argv);
254 LOG(INFO) << "Launching child process: "
255 << child_command_line.GetCommandLineString();
256 base::LaunchOptions launch_options;
257 base::Process child_process =
258 base::LaunchProcess(child_command_line, launch_options);
259 DCHECK(child_process.IsValid());
260 int exit_code = 0;
261 child_process.WaitForExit(&exit_code);
262 LOG(INFO) << "Child process returned " << exit_code;
263
264 // Toggle the image back if need be.
265 if (toggle_back) {
266 // The assumption is that work was in place and the bit was previously
267 // toggled.
268 DCHECK_NE(is_laa, is_laa_);
269 DCHECK_EQ(child_image.value(), image_.value());
270 LOG(INFO) << "Toggling back LargeAddressAware bit.";
271 if (!ToggleLaaBit(child_image))
272 return 1;
273 }
274
275 // Return the exit code of the child process.
276 return exit_code;
277 }
278
279 } // namespace runlaa
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698