The Pedigree Project 0.1
vm-permission-regressions.cc
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7
8#include "pedigree/kernel/Log.h"
9#include "pedigree/kernel/process/Process.h"
10#include "pedigree/kernel/process/Scheduler.h"
11#include "pedigree/kernel/process/Thread.h"
12#include "pedigree/kernel/process/Uninterruptible.h"
13#include "pedigree/kernel/processor/MemoryRegion.h"
14#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
15#include "pedigree/kernel/processor/Processor.h"
16#include "pedigree/kernel/processor/VirtualAddressSpace.h"
17#include "pedigree/kernel/syscallError.h"
18#include "pedigree/kernel/utilities/utility.h"
19
20#include "modules/subsys/posix/file-syscalls.h"
21#include "modules/system/vfs/File.h"
23#include <sys/mman.h>
24
25namespace {
26bool check(bool condition, const char* detail) {
27 if (!condition) {
28 ERROR("VM-OWNERSHIP-TEST: FAIL " << detail);
29 }
30 return condition;
31}
32
33#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
34bool protectedClone(bool inaccessible) {
35 VirtualAddressSpace& originalSpace = Processor::information().getVirtualAddressSpace();
37 const size_t pageSize = PhysicalMemoryManager::getPageSize();
38 Process* sourceProcess = new Process(Scheduler::instance().getKernelProcess(), true);
39 VirtualAddressSpace* source = sourceProcess->getAddressSpace();
40 void* address = reinterpret_cast<void*>(source->getDynamicStart() + 64 * pageSize);
41 const physical_uintptr_t physical = memory.allocatePage();
42 if (!physical || !source->map(physical, address, VirtualAddressSpace::Write)) {
43 if (physical) {
44 memory.freePage(physical);
45 }
46 delete sourceProcess;
47 return check(false, "private mapping setup");
48 }
50 volatile uint8_t* bytes = reinterpret_cast<volatile uint8_t*>(address);
51 bytes[0] = 0x31;
52 bytes[pageSize - 1] = 0x7A;
53 source->setFlags(address, inaccessible ? VirtualAddressSpace::NoAccess : 0);
54 Process* childProcess = new Process(sourceProcess, true);
55 VirtualAddressSpace* child = childProcess->getAddressSpace();
56
57 physical_uintptr_t parentPhysical = 0;
58 physical_uintptr_t childPhysical = 0;
59 size_t parentFlags = 0;
60 size_t childFlags = 0;
61 source->getMapping(address, parentPhysical, parentFlags);
62 child->getMapping(address, childPhysical, childFlags);
63 bool passed =
64 check(source->isMapped(address) && child->isMapped(address) && parentPhysical == physical &&
65 childPhysical == physical && (parentFlags & VirtualAddressSpace::CopyOnWrite) &&
66 (childFlags & VirtualAddressSpace::CopyOnWrite) &&
69 !(parentFlags & VirtualAddressSpace::KernelMode) &&
70 bool(parentFlags & VirtualAddressSpace::NoAccess) == inaccessible,
71 "protected clone changed ownership or permissions");
72 passed &= check(!source->handleCopyOnWriteFault(address, true) &&
73 !source->handleCopyOnWriteFault(address, false) &&
74 !child->handleCopyOnWriteFault(address, true) &&
75 PhysicalMemoryManager::pageReferenceCountForTest(physical) == 2,
76 "copy-on-write bypassed read-only protection");
77
78 source->setFlags(address, parentFlags & ~(VirtualAddressSpace::NoAccess |
80 bool parentResolved = source->handleCopyOnWriteFault(address, true);
81 passed &= check(parentResolved, "parent write upgrade could not resolve");
82 if (parentResolved) {
83 passed &= check(bytes[0] == 0x31 && bytes[pageSize - 1] == 0x7A,
84 "restoring parent access lost contents");
85 bytes[0] = 0xA1;
86 }
87 source->getMapping(address, parentPhysical, parentFlags);
88
90 child->setFlags(
92 bool childResolved = child->handleCopyOnWriteFault(address, true);
93 passed &= check(childResolved, "child write upgrade could not resolve");
94 if (childResolved) {
95 passed &= check(bytes[0] == 0x31 && bytes[pageSize - 1] == 0x7A,
96 "parent write changed protected child");
97 bytes[0] = 0xB1;
98 }
99 child->getMapping(address, childPhysical, childFlags);
101 if (parentResolved) {
102 passed &= check(bytes[0] == 0xA1, "child write changed parent");
103 }
104 Processor::switchAddressSpace(originalSpace);
105 delete childProcess;
106 delete sourceProcess;
107 passed &= check(PhysicalMemoryManager::pageReferenceCountForTest(physical) == 0 &&
108 PhysicalMemoryManager::pageReferenceCountForTest(parentPhysical) == 0 &&
109 PhysicalMemoryManager::pageReferenceCountForTest(childPhysical) == 0,
110 "protected clones leaked a physical owner");
111 return passed;
112}
113
114bool borrowedClones() {
115 VirtualAddressSpace& originalSpace = Processor::information().getVirtualAddressSpace();
117 const size_t pageSize = PhysicalMemoryManager::getPageSize();
118 Process* sourceProcess = new Process(Scheduler::instance().getKernelProcess(), true);
119 VirtualAddressSpace* source = sourceProcess->getAddressSpace();
120 void* address = reinterpret_cast<void*>(source->getDynamicStart() + 64 * pageSize);
121 const physical_uintptr_t physical = memory.allocatePage();
122 if (!physical || !source->map(physical, address,
125 if (physical) {
126 memory.freePage(physical);
127 }
128 delete sourceProcess;
129 return check(false, "borrowed mapping setup");
130 }
131 const size_t references = PhysicalMemoryManager::pageReferenceCountForTest(physical);
133 Process* first = new Process(sourceProcess, true);
134 Process* second = new Process(sourceProcess, true);
135 physical_uintptr_t clonePhysical = 0;
136 size_t cloneFlags = 0;
137 first->getAddressSpace()->getMapping(address, clonePhysical, cloneFlags);
138 bool passed = check(clonePhysical == physical && (cloneFlags & VirtualAddressSpace::Borrowed) &&
139 (cloneFlags & VirtualAddressSpace::NoAccess) &&
140 !(cloneFlags & VirtualAddressSpace::CopyOnWrite) &&
141 PhysicalMemoryManager::pageReferenceCountForTest(physical) == references,
142 "borrowed clones acquired physical ownership");
143 passed &= check(
144 !source->detachMapping(address, clonePhysical, cloneFlags, VirtualAddressSpace::Write) &&
145 clonePhysical == physical && (cloneFlags & VirtualAddressSpace::Borrowed) &&
146 source->isMapped(address),
147 "conditional detach changed a mismatched mapping");
148 passed &= check(first->getAddressSpace()->detachMapping(address, clonePhysical, cloneFlags,
150 clonePhysical == physical && (cloneFlags & VirtualAddressSpace::NoAccess) &&
151 !first->getAddressSpace()->isMapped(address) && source->isMapped(address) &&
152 PhysicalMemoryManager::pageReferenceCountForTest(physical) == references,
153 "foreign protected detach changed backing ownership");
154 Processor::switchAddressSpace(originalSpace);
155 delete second;
156 delete first;
157 delete sourceProcess;
158 passed &= check(PhysicalMemoryManager::pageReferenceCountForTest(physical) == references,
159 "borrowed teardown released backing ownership");
160 memory.freePage(physical);
161 passed &= check(PhysicalMemoryManager::pageReferenceCountForTest(physical) == 0,
162 "backing owner could not release borrowed page");
163 return passed;
164}
165#endif
166
167class ResizeProbeFile final : public File {
168 public:
169 explicit ResizeProbeFile(size_t pages = 2)
170 : File(String("mapped-resize-probe"), 0, 0, 0, 1, nullptr,
171 pages * PhysicalMemoryManager::getPageSize(), nullptr),
172 storage("Mapped Resize Probe"),
173 rejectResize(true),
174 rejectWritableMapping(false),
175 preparedLoans(0),
176 committedLoans(0),
177 shrinkCommits(0),
178 rejectSync(false),
179 syncCalls(0) {}
180
181 bool initialise() {
182 if (!PhysicalMemoryManager::instance().allocateRegion(
183 storage, getSize() / PhysicalMemoryManager::getPageSize(), 0,
185 return false;
186 }
187 ByteSet(storage.virtualAddress(), 0x49, getSize());
188 return true;
189 }
190
191 using File::sync;
192 bool sync(size_t, bool) override {
193 ++syncCalls;
194 return !rejectSync;
195 }
196 size_t loans() {
197 return __atomic_load_n(&physicalPageLoans(), __ATOMIC_ACQUIRE);
198 }
199 bool prepareSharedMapping(size_t, size_t) override {
200 if (rejectWritableMapping) {
201 SYSCALL_ERROR(OutOfMemory);
202 return false;
203 }
204 return true;
205 }
206
207 MemoryRegion storage;
208 bool rejectResize;
209 bool rejectWritableMapping;
210 size_t preparedLoans;
211 size_t committedLoans;
212 size_t shrinkCommits;
213 bool rejectSync;
214 size_t syncCalls;
215
216 protected:
217 uintptr_t readBlock(uint64_t location) override {
218 return reinterpret_cast<uintptr_t>(storage.virtualAddress()) + location;
219 }
220 bool pinBlock(uint64_t) override {
221 return true;
222 }
223 void unpinBlock(uint64_t) override {}
224 class ShrinkPlan final : public File::PreparedShrink {
225 public:
226 ShrinkPlan(ResizeProbeFile& file, size_t size) : file(file), size(size) {}
227 void commit() override {
228 file.committedLoans = file.loans();
229 ++file.shrinkCommits;
230 file.setSize(size);
231 }
232 ResizeProbeFile& file;
233 size_t size;
234 };
235 bool prepareShrink(const ShrinkContext& context,
236 UniquePointer<PreparedShrink>& prepared) override {
237 preparedLoans = loans();
238 if (rejectResize) {
239 SYSCALL_ERROR(IoError);
240 return false;
241 }
242 prepared = UniquePointer<PreparedShrink>::adopt(new ShrinkPlan(*this, context.newSize));
243 if (!prepared) {
244 SYSCALL_ERROR(OutOfMemory);
245 return false;
246 }
247 return true;
248 }
249 bool resizeFile(size_t size) override {
250 if (rejectResize) {
251 SYSCALL_ERROR(IoError);
252 return false;
253 }
254 setSize(size);
255 return true;
256 }
257};
258
259#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
260int resizeFailureWorker(void* parameter) {
261 bool& passed = *static_cast<bool*>(parameter);
262 const size_t pageSize = PhysicalMemoryManager::getPageSize();
263 ResizeProbeFile file;
264 if (!file.initialise()) {
265 return 0;
266 }
268 uintptr_t shared = 0;
269 uintptr_t privateAddress = 0;
270 auto permissions = MemoryMappedObject::Read | MemoryMappedObject::Write;
271 MemoryMappedObject* sharedObject =
272 manager.mapFile(&file, shared, 2 * pageSize, MemoryMappedObject::Read, 0, false);
273 MemoryMappedObject* privateObject =
274 manager.mapFile(&file, privateAddress, 2 * pageSize, permissions);
275 if (!sharedObject || !privateObject || !manager.faultIn(shared, false) ||
276 !manager.faultIn(shared + pageSize, false) ||
277 !manager.faultIn(privateAddress + pageSize, true)) {
278 manager.removeAndRelease(shared, sharedObject ? 2 * pageSize : 0);
279 manager.removeAndRelease(privateAddress, privateObject ? 2 * pageSize : 0);
280 manager.unmapAll();
281 return 0;
282 }
283 volatile uint8_t* privateByte = reinterpret_cast<volatile uint8_t*>(privateAddress + pageSize);
284 *privateByte = 0xA5;
285 VirtualAddressSpace& space = Processor::information().getVirtualAddressSpace();
286 physical_uintptr_t owned = 0;
287 size_t flags = 0;
288 file.rejectWritableMapping = true;
290 passed = check(!manager.setPermissions(shared, 2 * pageSize, permissions, &status) &&
291 status == MemoryMapManager::ProtectStatus::NoMemory,
292 "writable protection ignored failed backing preparation");
293 space.getMapping(reinterpret_cast<void*>(shared + pageSize), owned, flags);
294 passed &= check(!(flags & VirtualAddressSpace::Write),
295 "failed backing preparation published write permission");
296 file.rejectWritableMapping = false;
297 passed &= check(manager.setPermissions(shared, 2 * pageSize, permissions) != 0,
298 "writable protection could not retry backing preparation");
299 space.getMapping(const_cast<uint8_t*>(privateByte), owned, flags);
300 const size_t privateFlags = flags;
301 physical_uintptr_t borrowed = 0;
302 size_t borrowedFlags = 0;
303 physical_uintptr_t prefix = 0;
304 size_t prefixFlags = 0;
305 space.getMapping(reinterpret_cast<void*>(shared + pageSize), borrowed, borrowedFlags);
306 space.getMapping(reinterpret_cast<void*>(shared), prefix, prefixFlags);
307 const size_t originalLoans = file.loans();
308 const size_t privateReferences = PhysicalMemoryManager::pageReferenceCountForTest(owned);
309 passed &= check(originalLoans >= 2 && (borrowedFlags & VirtualAddressSpace::Borrowed) &&
310 (prefixFlags & VirtualAddressSpace::Borrowed) && privateReferences == 1,
311 "shrink fixture did not establish borrowed and private ownership");
312 const bool rejected = !file.resize(pageSize);
313 physical_uintptr_t afterBorrowed = 0;
314 size_t afterBorrowedFlags = 0;
315 physical_uintptr_t afterPrivate = 0;
316 size_t afterPrivateFlags = 0;
317 const bool borrowedPresent = space.isMapped(reinterpret_cast<void*>(shared + pageSize));
318 const bool privatePresent = space.isMapped(const_cast<uint8_t*>(privateByte));
319 if (borrowedPresent)
320 space.getMapping(reinterpret_cast<void*>(shared + pageSize), afterBorrowed, afterBorrowedFlags);
321 if (privatePresent)
322 space.getMapping(const_cast<uint8_t*>(privateByte), afterPrivate, afterPrivateFlags);
323 passed &= check(
324 rejected && !file.shrinkCommits && file.preparedLoans == originalLoans &&
325 file.loans() == originalLoans && file.getSize() == 2 * pageSize && borrowedPresent &&
326 afterBorrowed == borrowed && afterBorrowedFlags == borrowedFlags && privatePresent &&
327 afterPrivate == owned && afterPrivateFlags == privateFlags && *privateByte == 0xA5 &&
328 PhysicalMemoryManager::pageReferenceCountForTest(owned) == privateReferences,
329 "failed backend preparation changed a suffix PTE, loan, or private page");
330 file.rejectResize = false;
331 const bool resized = file.resize(pageSize);
332 physical_uintptr_t afterPrefix = 0;
333 size_t afterPrefixFlags = 0;
334 const bool prefixPresent = space.isMapped(reinterpret_cast<void*>(shared));
335 if (prefixPresent)
336 space.getMapping(reinterpret_cast<void*>(shared), afterPrefix, afterPrefixFlags);
337 passed &=
338 check(resized && file.shrinkCommits == 1 && file.preparedLoans == originalLoans &&
339 file.committedLoans == 1 && file.loans() == 1 && prefixPresent &&
340 afterPrefix == prefix && afterPrefixFlags == prefixFlags &&
341 file.getSize() == pageSize && !space.isMapped(const_cast<uint8_t*>(privateByte)) &&
342 !space.isMapped(reinterpret_cast<void*>(shared + pageSize)) &&
343 !manager.faultIn(privateAddress + pageSize, false) &&
344 !manager.faultIn(shared + pageSize, false) &&
345 PhysicalMemoryManager::pageReferenceCountForTest(owned) == 0,
346 "shrink commit lost the prefix or retained suffix ownership");
347 manager.removeAndRelease(shared, 2 * pageSize);
348 manager.removeAndRelease(privateAddress, 2 * pageSize);
349 manager.unmapAll();
350 return 0;
351}
352
353bool failedMappedResize() {
354 Process* process = new Process(Scheduler::instance().getKernelProcess(), true);
355 bool passed = false;
356 Thread* worker = new Thread(process, resizeFailureWorker, &passed, nullptr, false, true, true);
357 const bool started = worker->start();
358 const bool joined = started && worker->joinForCompletion();
359 if (!started) {
360 delete worker;
361 }
362 delete process;
363 return check(started && joined && passed, "mapped resize backend failure fixture");
364}
365#endif
366
367int sparseSplitWorker(void* parameter) {
368 bool& passed = *static_cast<bool*>(parameter);
369 const size_t pageSize = PhysicalMemoryManager::getPageSize();
371 MemoryMapManager::OperationGuard operation(manager);
372 VirtualAddressSpace& space = Processor::information().getVirtualAddressSpace();
373 ResizeProbeFile file(3);
374 if (!file.initialise()) {
375 return 0;
376 }
377 passed = true;
378 const unsigned masks[] = {4, 2, 5, 7};
379 for (unsigned mask : masks) {
380 NOTICE("VM-OWNERSHIP-TEST: BEGIN sparse-split mask=" << mask);
381 uintptr_t address = 0;
382 MemoryMappedObject* object =
383 manager.mapFile(&file, address, 3 * pageSize, MemoryMappedObject::Read, 0, false);
384 if (!check(object != nullptr, "sparse file setup")) {
385 passed = false;
386 break;
387 }
388 size_t expectedLoans = 0;
389 for (size_t page = 3; page; --page) {
390 if (mask & (1U << (page - 1))) {
391 passed &= check(manager.faultIn(address + (page - 1) * pageSize, false),
392 "sparse resident page setup");
393 ++expectedLoans;
394 }
395 }
396 passed &= check(file.loans() == expectedLoans, "sparse backing loan count before split");
397 passed &= check(
398 manager.setPermissions(address + pageSize, 2 * pageSize, MemoryMappedObject::Read) != 0,
399 "sparse protection split");
400 passed &= check(file.loans() == expectedLoans, "sparse split changed backing loans");
401 passed &= check(manager.removeAndRelease(address, 3 * pageSize) == 2,
402 "sparse removal did not visit both objects");
403 passed &= check(!manager.contains(address, 3 * pageSize), "sparse objects survived removal");
404 passed &= check(file.loans() == 0, "sparse removal retained backing loans");
405 for (size_t page = 0; page < 3; ++page) {
406 passed &= check(!space.isMapped(reinterpret_cast<void*>(address + page * pageSize)),
407 "sparse removal retained a PTE");
408 }
409 const uintptr_t requested = address;
410 MemoryMapManager::MapStatus status;
411 object = manager.mapAnon(address, 3 * pageSize, MemoryMappedObject::Read,
412 MemoryMapManager::Placement::FixedNoReplace, &status);
413 passed &=
414 check(object && address == requested && status == MemoryMapManager::MapStatus::Success,
415 "sparse removal retained a reservation");
416 if (object) {
417 for (size_t page = 0; page < 3; ++page) {
418 const uintptr_t at = address + page * pageSize;
419 if (manager.faultIn(at, false)) {
420 const volatile uint8_t* bytes = reinterpret_cast<const volatile uint8_t*>(at);
421 for (size_t byte = 0; byte < pageSize; ++byte) {
422 if (!check(bytes[byte] == 0, "sparse anonymous reuse retained file contents")) {
423 ERROR("VM-OWNERSHIP-TEST: byte=" << page * pageSize + byte
424 << " value=" << static_cast<unsigned>(bytes[byte]));
425 passed = false;
426 break;
427 }
428 }
429 } else {
430 passed &= check(false, "sparse anonymous reuse could not fault");
431 }
432 }
433 manager.removeAndRelease(address, 3 * pageSize);
434 }
435 if (!passed) {
436 break;
437 }
438 NOTICE("VM-OWNERSHIP-TEST: PASS sparse-split mask="
439 << mask << " objects=0 ptes=0 loans=0 reservation=reused zero=verified");
440 }
441 manager.unmapAll();
442 return 0;
443}
444
445bool sparseSplitOwnership() {
446 Process* process = new Process(Scheduler::instance().getKernelProcess(), true);
447 bool passed = false;
448 Thread* worker = new Thread(process, sparseSplitWorker, &passed, nullptr, false, true, true);
449 const bool started = worker->start();
450 const bool joined = started && worker->joinForCompletion();
451 if (!started) {
452 delete worker;
453 }
454 delete process;
455 return check(started && joined && passed, "sparse split ownership fixture");
456}
457
458int checkedSyncWorker(void* parameter) {
459 bool& passed = *static_cast<bool*>(parameter);
460 const size_t pageSize = PhysicalMemoryManager::getPageSize();
462 MemoryMapManager::OperationGuard operation(manager);
463 ResizeProbeFile file(3);
464 if (!file.initialise()) {
465 return 0;
466 }
467 uintptr_t address = 0;
468 MemoryMappedObject* object =
469 manager.mapFile(&file, address, 3 * pageSize, MemoryMappedObject::Read, 0, false);
470 if (!object || !manager.faultIn(address, false) || !manager.faultIn(address + pageSize, false)) {
471 manager.unmapAll();
472 return 0;
473 }
474 manager.removeAndRelease(address + 2 * pageSize, pageSize);
475 Thread* thread = Processor::information().getCurrentThread();
476 void* mapping = reinterpret_cast<void*>(address);
477 file.rejectSync = true;
478 thread->setErrno(0);
479 passed = check(posix_msync(mapping, 3 * pageSize, MS_SYNC) == -1 &&
480 thread->getErrno() == Error::OutOfMemory && file.syncCalls == 0,
481 "msync range failure reached backing I/O or lost ENOMEM");
482 thread->setErrno(0);
483 passed &= check(posix_msync(mapping, 2 * pageSize, MS_SYNC) == -1 &&
484 thread->getErrno() == Error::IoError && file.syncCalls == 2,
485 "msync lost EIO or skipped a page after the first backend failure");
486 file.rejectSync = false;
487 thread->setErrno(0);
488 passed &= check(posix_msync(mapping, 2 * pageSize, MS_SYNC) == 0 && file.syncCalls == 4,
489 "msync could not retry both failed pages");
490 manager.removeAndRelease(address, 2 * pageSize);
491 manager.unmapAll();
492 if (passed) {
493 NOTICE(
494 "VM-OWNERSHIP-TEST: PASS checked-msync range=ENOMEM range-io=0 failure=EIO "
495 "attempted=2 retry=2");
496 }
497 return 0;
498}
499
500bool checkedMappedSync() {
501 Process* process = new Process(Scheduler::instance().getKernelProcess(), true);
502 bool passed = false;
503 Thread* worker = new Thread(process, checkedSyncWorker, &passed, nullptr, false, true, true);
504 const bool started = worker->start();
505 const bool joined = started && worker->joinForCompletion();
506 if (!started) {
507 delete worker;
508 }
509 delete process;
510 return check(started && joined && passed, "checked mapped sync fixture");
511}
512} // namespace
513
514bool runVmMappedOwnershipRegressions() {
515 NOTICE("VM-OWNERSHIP-TEST: BEGIN sparse-split-ownership");
516 if (!sparseSplitOwnership()) {
517 return false;
518 }
519 NOTICE("VM-OWNERSHIP-TEST: PASS sparse-split-ownership cases=4");
520 NOTICE("VM-OWNERSHIP-TEST: BEGIN checked-msync");
521 if (!checkedMappedSync()) {
522 return false;
523 }
524 NOTICE("VM-OWNERSHIP-TEST: PASS mapped-ownership");
525 return true;
526}
527
528#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
529bool runHostedVmOperationGuardRegressions() {
531 Thread* thread = Processor::information().getCurrentThread();
532 const bool priorEvents = thread->eventsDeferred();
533 const bool priorTermination = thread->isTerminationDeferred();
534 auto protectedState = [&]() {
535 return thread->eventsDeferred() && thread->isTerminationDeferred();
536 };
537 auto restoredState = [&]() {
538 return thread->eventsDeferred() == priorEvents &&
539 thread->isTerminationDeferred() == priorTermination;
540 };
541 bool passed = true;
542 {
543 MemoryMapManager::OperationGuard operation(manager);
544 passed &= check(operation && protectedState(), "mapping operation lost deferral protection");
545 {
546 MemoryMapManager::OperationGuard nestedOperation(manager);
547 passed &= check(nestedOperation && protectedState(),
548 "nested mapping operation lost deferral protection");
549 {
550 MemoryMapManager::OperationGuard pressure(manager, true);
551 passed &= check(!pressure && protectedState(),
552 "failed pressure entry changed mapping operation protection");
553 }
554 passed &= check(protectedState(), "failed pressure guard retired outer protection");
555 }
556 passed &= check(protectedState(), "nested mapping guard retired outer protection");
557 }
558 passed &= check(restoredState(), "mapping operation did not restore prior deferral state");
559 {
560 MemoryMapManager::OperationGuard pressure(manager, true);
561 passed &= check(pressure && protectedState(),
562 "successful pressure entry lacked the operation gate or protection");
563 }
564 passed &= check(restoredState(), "pressure guard did not restore prior deferral state");
565 {
566 Uninterruptible outer;
567 {
568 MemoryMapManager::OperationGuard operation(manager);
569 passed &= check(operation && protectedState(),
570 "mapping operation lost enclosing uninterruptible protection");
571 }
572 passed &= check(protectedState(), "mapping guard retired enclosing uninterruptible scope");
573 }
574 passed &= check(restoredState(), "enclosing scope did not restore prior deferral state");
575 if (passed) {
576 NOTICE("HOSTED-WAIT-TEST: PASS vm-operation-guard-deferrals");
577 }
578 return passed;
579}
580
581bool runHostedVmPermissionRegressions() {
582 VirtualAddressSpace& space = Processor::information().getVirtualAddressSpace();
583 bool passed = check(space.isAddressValid(reinterpret_cast<void*>(0x00007FFFFFFFFFFFULL)) &&
584 !space.isAddressValid(reinterpret_cast<void*>(0x0000800000000000ULL)) &&
585 !space.isAddressValid(reinterpret_cast<void*>(0xFFFF7FFFFFFFFFFFULL)) &&
586 space.isAddressValid(reinterpret_cast<void*>(0xFFFF800000000000ULL)),
587 "four-level canonical address boundaries");
588 passed &= runHostedVmOperationGuardRegressions();
589 passed &= protectedClone(false);
590 passed &= protectedClone(true);
591 passed &= borrowedClones();
592 passed &= failedMappedResize();
593 passed &= runVmMappedOwnershipRegressions();
594 if (passed) {
595 NOTICE("HOSTED-WAIT-TEST: PASS vm-permission-ownership");
596 }
597 return passed;
598}
599#endif
Memory-mapped file interface.
Definition File.h:74
virtual uintptr_t readBlock(uint64_t location)
Definition File.cc:1247
virtual void unpinBlock(uint64_t location)
Definition File.cc:1298
virtual bool sync()
Definition File.cc:519
virtual bool prepareSharedMapping(size_t offset, size_t length)
Definition File.cc:1015
virtual MUST_USE_RESULT bool pinBlock(uint64_t location)
Definition File.cc:1279
MemoryMappedObject * mapAnon(uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms)
static MemoryMapManager & instance()
MemoryMappedObject * mapFile(File *pFile, uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms, size_t offset=0, bool bCopyOnWrite=true)
size_t removeAndRelease(uintptr_t base, size_t length, VmStatus *status=nullptr)
bool contains(uintptr_t base, size_t length)
Special memory entity in the kernel's virtual address space.
void * virtualAddress() const
virtual physical_uintptr_t allocatePage(size_t pageConstraints=0)=0
static PhysicalMemoryManager & instance()
virtual void freePage(physical_uintptr_t page)=0
VirtualAddressSpace * getAddressSpace()
Definition Process.h:478
static ProcessorInformation & information()
static void switchAddressSpace(VirtualAddressSpace &AddressSpace)
static Scheduler & instance()
Definition Scheduler.h:96
void setErrno(size_t err)
Definition Thread.h:478
size_t getErrno()
Definition Thread.h:473
bool eventsDeferred() const
Definition Thread.cc:3180
bool isTerminationDeferred() const
Definition Thread.h:565
static UniquePointer< T > adopt(T *pointer)
Definition Pointers.h:101
virtual void setFlags(void *virtualAddress, size_t newFlags)=0
virtual bool map(physical_uintptr_t physicalAddress, void *virtualAddress, size_t flags)=0
virtual bool isMapped(void *virtualAddress)=0
virtual bool handleCopyOnWriteFault(void *virtualAddress, bool userMode)=0
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
virtual bool detachMapping(void *virtualAddress, physical_uintptr_t &physical, size_t &flags, size_t requiredFlags=0)
virtual bool isAddressValid(void *virtualAddress)=0