/*
         * Load the credential for an application.
         *
         * Currently ExpressOS does not deal with key management, etc.
         * It uses a static key for all application.
         */
        internal static Credential GetCredential(ASCIIString name, Process process)
        {
            Contract.Ensures(Contract.Result<Credential>().GhostOwner == process);
            var key = new byte[] { 0xc8, 0x43, 0xae, 0x85, 0x9b, 0x63, 0x4b, 0x72, 0x1b, 0x14, 0xcf, 0x6d, 0xa8, 0xf9, 0x6f, 0x1d };

            // 1003 == AID_GRAPHICS
            return new Credential(process, 1003, key);
        }
Exemple #2
0
        // Create an empty user-space memory region
        // reserve the first page as well as the kernel space
        public static MemoryRegion CreateUserSpaceRegion(Process owner)
        {
            Contract.Ensures(Contract.Result<MemoryRegion>().GhostOwner == owner);

            var r = new MemoryRegion(owner, 0, 0, null, 0, 0, Pointer.Zero, Arch.ArchDefinition.PageSize, true);
            var r1 = new MemoryRegion(owner, 0, 0, null, 0, 0, new Pointer(AddressSpace.KERNEL_OFFSET), AddressSpace.KERNEL_SIZE, true);
            r.Next = r1;
            return r;
        }
Exemple #3
0
        internal static CachePage Allocate(Process owner, int loc)
        {
            Contract.Requires(loc >= 0);
            Contract.Ensures(Contract.Result<CachePage>().CurrentState == State.Empty);
            Contract.Ensures(Contract.Result<CachePage>().Next == null);

            var buf = AllocFreeBuffer();
            var p = new CachePage(owner, loc, buf);
            return p;
        }
Exemple #4
0
        public AddressSpace(Process owner, Arch.ArchAddressSpace impl)
        {
            Contract.Ensures(GhostOwner == owner);
            Contract.Ensures(Head.GhostOwner == GhostOwner);

            this.impl = impl;
            workingSet = new TableWorkingSet();
            Head = MemoryRegion.CreateUserSpaceRegion(owner);
            this.GhostOwner = owner;
        }
Exemple #5
0
        public File(Process owner, GenericINode inode, int flags, int mode)
        {
            Contract.Ensures(GhostOwner == owner);

            this.inode = inode;
            this.flags = flags;
            this.mode = mode;
            this.position = 0;
            this.GhostOwner = owner;
            inode.IncreaseRefCount();
        }
Exemple #6
0
        private Thread(Arch.ArchThread impl, Process parent)
        {
            Contract.Requires(parent != null);
            Contract.Ensures(Parent == parent);
            Contract.Ensures(VBinderState.Owner == this);

            this.impl = impl;
            this.Parent = parent;
            this.TLSArray = Arch.NativeMethods.l4api_tls_array_alloc();
            this.VBinderState = new VBinderThreadState(this);
        }
Exemple #7
0
        public static int Parse(int helperPid, File file, Process proc, ref UserPtr stackTop)
        {
            Contract.Requires(file.GhostOwner == proc);
            Contract.Requires(proc.Space.GhostOwner == proc);

            var buf = new byte[Size];
            uint pos = 0;
            if (file.Read(buf, ref pos) != buf.Length)
                return -ErrorCode.EINVAL;

            var eh = Read(buf);
            if (eh.type != ELF32Header.ELF_TYPE_EXECUTABLE || eh.ProgramHeaderOffest == 0)
                return -ErrorCode.ENOEXEC;

            proc.EntryPoint = eh.EntryPoint;

            ELF32ProgramHeader ph = new ELF32ProgramHeader();
            var ret = FindInterpreter(file, eh, ref ph);
            if (ret == -ErrorCode.EINVAL)
            {
                Arch.Console.WriteLine("Malformed ELF file");
                return ret;
            }
            else if (ret == 0)
            {
                var interpreterBuf = new byte[ph.FileSize];
                pos = ph.offset;
                if (file.Read(interpreterBuf, ref pos) != interpreterBuf.Length)
                    return -ErrorCode.EINVAL;

                var interpreterName = new ASCIIString(interpreterBuf);
                ErrorCode ec;
                var interpreter_inode = Arch.ArchFS.Open(helperPid, interpreterName, 0, 0, out ec);
                if (interpreter_inode == null)
                    return -ErrorCode.ENOENT;

                var interpreter = new File(proc, interpreter_inode, FileFlags.ReadOnly, 0);

                /*
                 * Parse the information of linker.
                 *
                 * This function will also override the entry point.
                 */
                if (Parse(helperPid, interpreter, proc, ref stackTop) != 0)
                    return -ErrorCode.EINVAL;

                // So now let's copy the program header to the top of the stack, and push auxlirary vectors
                PushProgramHeaderAndAuxliraryVectors(proc, file, eh, ref stackTop);
            }

            return MapInSegments(file, proc, eh);
        }
        public FileDescriptorTable(Process owner)
        {
            Contract.Ensures(GhostOwner == owner);

            descriptors = new File[DEFAULT_SIZE];
            for (var i = 0; i < descriptors.Length; ++i)
            {
                descriptors[i] = null;
            }

            finger = FD_START;
            this.GhostOwner = owner;
        }
Exemple #9
0
        private CachePage(Process owner, int location, ByteBufferRef buf)
        {
            Contract.Requires(location >= 0);
            Contract.Ensures(Owner == owner);
            Contract.Ensures(Location == location);
            Contract.Ensures(CurrentState == State.Empty);
            Contract.Ensures(Next == null);

            this.Owner = owner;
            this.Location = location;
            this.Buffer = buf;
            this.Next = null;
            this.CurrentState = State.Empty;
        }
        public void OnActiveProcessChanged(Process newActiveProcess)
        {
            Contract.Requires(newActiveProcess != null);
            Contract.Ensures(ActiveProcess == newActiveProcess);

            if (ActiveProcess == newActiveProcess)
                return;

            if (ActiveProcess != null)
            {
                if (ActiveProcess.ScreenEnabled)
                    DisableScreen();

                ActiveProcess = null;
            }

            EnableScreen(newActiveProcess);
        }
        internal MemoryRegion(Process owner, uint access, int flags, File file, uint fileOffset, int fileSize, Pointer vaddr, int size, bool isFixed)
        {
            Contract.Requires(file == null || file.GhostOwner == owner);
            Contract.Ensures(GhostOwner == owner);

            this.Access = access;
            this.Flags = flags;
            this.BackingFile = file;
            this.FileOffset = fileOffset;
            this.FileSize = fileSize;
            this.StartAddress = vaddr;
            this.Size = size;
            this.Next = null;
            this.IsFixed = isFixed;
            this.GhostOwner = owner;

            if (file != null)
                file.inode.IncreaseRefCount();
        }
Exemple #12
0
 internal unsafe int Write(Process proc, uint[] val)
 {
     fixed (uint *p = &val[0]) {
         return Write(proc, new Pointer(p), val.Length * sizeof(uint));
     }
 }
Exemple #13
0
 internal int Write(Process proc, byte[] val)
 {
     var b = new ByteBufferRef(val);
     return Write(proc, b);
 }
Exemple #14
0
 internal unsafe int Write(Process proc, int v)
 {
     var p = v;
     return Write(proc, new Pointer(&p), sizeof(int));
 }
Exemple #15
0
        //
        // We do sync read for this one, since it's simpler..
        //
        public static Process CreateProcess(ASCIIString path, ASCIIString[] argv, ASCIIString[] envp, AndroidApplicationInfo appInfo)
        {
            var proc = new Process(path, appInfo);
            Utils.Assert(!proc.Space.impl._value.isInvalid);

            uint addr;
            int workspace_fd;
            uint workspace_size;
            proc.helperPid = Arch.IPCStubs.linux_sys_take_helper(out addr, out workspace_fd, out workspace_size);

            if (proc.helperPid < 0)
            {
                Arch.Console.WriteLine("CreateProcess: cannot get helper");
                return null;
            }

            proc.ShadowBinderVMStart = addr;

            ErrorCode ec;
            var inode = Arch.ArchFS.Open(proc.helperPid, path, 0, 0, out ec);
            if (inode == null)
            {
                Arch.Console.WriteLine("CreateProcess: cannot open file");
                return null;
            }

            var stack_top = new UserPtr(INITIAL_STACK_LOCATION);

            // 4M Initial stack
            var stack_size = 4096 * Arch.ArchDefinition.PageSize;
            proc.Space.AddStackMapping(stack_top, stack_size);
            stack_top += stack_size;

            var augmented_envp = CreateEnvpArrayWithWorkspace(envp, proc, workspace_fd, workspace_size);

            var envp_ptr = PushCharArray(proc, augmented_envp, ref stack_top);
            if (envp_ptr == null)
            {
                Arch.Console.WriteLine("CreateProcess: Push envp failed");
                return null;
            }

            var argv_ptr = PushCharArray(proc, argv, ref stack_top);
            if (argv_ptr == null)
            {
                Arch.Console.WriteLine("CreateProcess: Push argv failed");
                return null;
            }

            stack_top = UserPtr.RoundDown(stack_top);

            // Parse the ELF file, which might push additional info on to the stack
            // (i.e., aux vectors when the ELF is dynamically linked)
            var file = new File(proc, inode, FileFlags.ReadWrite, 0);

            int ret = ELF32Header.Parse(proc.helperPid, file, proc, ref stack_top);

            if (ret != 0)
            {
                Arch.Console.WriteLine("CreateProcess: Parse ELF file failed");
                return null;
            }

            //%esp         The stack contains the arguments and environment:
            //     0(%esp)                 argc
            //     4(%esp)                 argv[0]
            //     ...
            //     (4*argc)(%esp)          NULL
            //     (4*(argc+1))(%esp)      envp[0]
            //     ...
            //                             NULL
            if (PushArgumentPointers(proc, envp_ptr, ref stack_top) != 0)
                return null;

            if (PushArgumentPointers(proc, argv_ptr, ref stack_top) != 0)
                return null;

            if (PushInt(proc, argv_ptr.Length, ref stack_top) != 0)
                return null;

            // Stdio
            var file_stdout = File.CreateStdout(proc);
            Contract.Assume(proc.Files.IsAvailableFd(Process.STDOUT_FD));
            proc.InstallFd(Process.STDOUT_FD, file_stdout);

            var file_stderr = File.CreateStdout(proc);
            Contract.Assume(proc.Files.IsAvailableFd(Process.STDERR_FD));
            proc.InstallFd(Process.STDERR_FD, file_stderr);

            var mainThread = Thread.Create(proc);

            if (appInfo != null)
            {
                var p = appInfo.ToParcel();
                Globals.LinuxIPCBuffer.CopyFrom(0, p);
                Arch.IPCStubs.WriteAppInfo(proc.helperPid, p.Length);
            }

            // Start the main thread
            mainThread.Start(new Pointer(proc.EntryPoint), stack_top.Value);
            return proc;
        }
Exemple #16
0
        private static int PushProgramHeaderAndAuxliraryVectors(Process proc, File file, ELF32Header eh, ref UserPtr stackTop)
        {
            var programHeaderLength = eh.NumOfProgramHeader * eh.ProgramHeaderSize;
            var buf = new byte[programHeaderLength];
            uint pos = eh.ProgramHeaderOffest;

            if (file.Read(buf, ref pos) != programHeaderLength)
                return -ErrorCode.ENOMEM;

            stackTop -= programHeaderLength;
            UserPtr ph_ptr = stackTop;

            if (ph_ptr.Write(proc, buf) != 0)
                return -ErrorCode.ENOMEM;

            // align
            stackTop = UserPtr.RoundDown(stackTop);

            var aux_vector = new uint[LengthOfAuxVector];
            aux_vector[0] = AT_PHDR;
            aux_vector[1] = ph_ptr.Value.ToUInt32();
            aux_vector[2] = AT_ENTRY;
            aux_vector[3] = eh.EntryPoint;
            aux_vector[4] = AT_PHNUM;
            aux_vector[5] = eh.NumOfProgramHeader;
            aux_vector[6] = 0;
            aux_vector[7] = 0;

            var auxVectorSize = sizeof(uint) * LengthOfAuxVector;
            stackTop -= auxVectorSize;

            if (stackTop.Write(proc, aux_vector) != 0)
                return -ErrorCode.ENOMEM;

            return 0;
        }
Exemple #17
0
 int Write(Process proc, ByteBufferRef val)
 {
     return Write(proc, new Pointer(val.Location), val.Length);
 }
Exemple #18
0
        private static ASCIIString[] CreateEnvpArrayWithWorkspace(ASCIIString[] envp, Process proc, int workspace_fd, uint workspace_size)
        {
            var res = new ASCIIString[envp.Length + 1];
            for (int i = 0; i < envp.Length; ++i)
                res[i] = envp[i];

            var inode = new Arch.ArchINode(workspace_fd, workspace_size, proc.helperPid);

            var file = new File(proc, inode, FileFlags.ReadOnly, 0);

            var fd = proc.GetUnusedFd();
            proc.InstallFd(fd, file);

            var s = "ANDROID_PROPERTY_WORKSPACE=" + fd.ToString() + "," + workspace_size.ToString();
            res[envp.Length] = new ASCIIString(s);

            return res;
        }
        private void DisableScreen()
        {
            Contract.Requires(ActiveProcess != null);
            Contract.Requires(ActiveProcess.ScreenEnabled);
            Contract.Ensures(ActiveProcess == null);
            Contract.Ensures(!Contract.OldValue(ActiveProcess).ScreenEnabled);

            for (var r = ActiveProcess.Space.Head; r != null; r = r.Next)
            {
                if (r.BackingFile == null
                    || r.BackingFile.inode.kind != GenericINode.INodeKind.ScreenBufferINodeKind)
                    continue;

                r.UpdateAccessRights(ActiveProcess.Space, MemoryRegion.FAULT_READ);
            }
            ActiveProcess.ScreenEnabled = false;
            ActiveProcess = null;
        }
        private void EnableScreen(Process proc)
        {
            Contract.Requires(proc != null);
            Contract.Requires(ActiveProcess == null);
            Contract.Ensures(ActiveProcess == proc);
            Contract.Ensures(ActiveProcess.ScreenEnabled);

            for (var r = proc.Space.Head; r != null; r = r.Next)
            {
                if (r.BackingFile == null
                    || r.BackingFile.inode.kind != GenericINode.INodeKind.ScreenBufferINodeKind)
                    continue;

                r.UpdateAccessRights(proc.Space, MemoryRegion.FAULT_WRITE | MemoryRegion.FAULT_READ);
            }
            ActiveProcess = proc;
            ActiveProcess.ScreenEnabled = true;
        }
Exemple #21
0
 internal static File CreateStdout(Process proc)
 {
     Contract.Ensures(Contract.Result<File>().GhostOwner == proc);
     return new File(proc, ConsoleINode.Instance, FileFlags.WriteOnly, 0);
 }
Exemple #22
0
        internal static Thread Create(Process parent)
        {
            var impl = Arch.ArchThread.Create(parent.Space.impl);
            if (impl == null)
            {
                Arch.Console.WriteLine("create thread failed");
                return null;
            }

            var res = new Thread(impl, parent);
            Globals.Threads.Add(res);

            return res;
        }
Exemple #23
0
        private int Read(Process process, ByteBufferRef buffer, bool is_string)
        {
            var bytesLeft = is_string ? buffer.Length - 1 : buffer.Length;
            var bytesRead = 0;

            var src = _value;

            while (bytesLeft > 0)
            {
                var region = process.Space.Find(src);

                // Invalid mapping
                if (region == null || region.IsFixed)
                    break;

                var off = Arch.ArchDefinition.PageOffset(src.ToUInt32());
                var virtualAddr = process.Space.UserToVirt(new UserPtr(src));

                if (virtualAddr == Pointer.Zero)
                {
                    uint permission;

                    Pager.HandlePageFault(process, MemoryRegion.FAULT_MASK, _value, Pointer.Zero, out virtualAddr, out permission);

                    if (virtualAddr == Pointer.Zero)
                        break;
                }

                var virtual_page = Arch.ArchDefinition.PageIndex(virtualAddr.ToUInt32());
                var page_buf = new ByteBufferRef(new IntPtr(virtual_page), Arch.ArchDefinition.PageSize);

                var b = Arch.ArchDefinition.PageSize - off;
                var bytesTobeCopied = b > bytesLeft ? bytesLeft : b;

                var src_buf = page_buf.Slice(off, bytesTobeCopied);

                if (is_string)
                {
                    var res = Util.CopyString(ref buffer, bytesRead, src_buf);
                    bytesRead += res;

                    if (res < bytesTobeCopied)
                    {
                        // We're done.
                        break;
                    }
                }
                else
                {
                    for (var i = 0; i < bytesTobeCopied; ++i)
                    {
                        buffer.Set(i + bytesRead, src_buf.Get(i));
                    }
                    bytesRead += bytesTobeCopied;
                }

                bytesLeft -= bytesTobeCopied;
                src += bytesTobeCopied;
            }

            if (is_string)
            {
                buffer.Set(bytesRead, 0);
            }
            return bytesRead;
        }
Exemple #24
0
        private int Write(Process process, Pointer dst, int length)
        {
            var bytesLeft = length;

            var src = _value;
            var dst_buf = new ByteBufferRef(dst.ToIntPtr(), length);
            var cursor = 0;
            while (bytesLeft > 0)
            {
                var region = process.Space.Find(src);

                // Invalid mapping
                if (region == null || region.IsFixed)
                {
                    return bytesLeft;
                }

                var off = Arch.ArchDefinition.PageOffset(src.ToUInt32());
                var virtualAddr = process.Space.UserToVirt(new UserPtr(src));

                if (virtualAddr == Pointer.Zero)
                {
                    // Page isn't present, try to bring it in.
                    uint permission;

                    Pager.HandlePageFault(process, MemoryRegion.FAULT_MASK, src, Pointer.Zero, out virtualAddr, out permission);

                    if (virtualAddr == Pointer.Zero)
                        break;
                }

                var virtual_page = Arch.ArchDefinition.PageIndex(virtualAddr.ToUInt32());
                var page_buf = new ByteBufferRef(new IntPtr(virtual_page), Arch.ArchDefinition.PageSize);

                var b = Arch.ArchDefinition.PageSize - off;
                var bytesTobeCopied = b > bytesLeft ? bytesLeft : b;

                var dst_buf_page = page_buf.Slice(off, bytesTobeCopied);
                for (var i = 0; i < bytesTobeCopied; ++i)
                {
                    dst_buf_page.Set(i, dst_buf.Get(cursor + i));
                }

                bytesLeft -= bytesTobeCopied;
                src += bytesTobeCopied;
                cursor += bytesTobeCopied;
            }

            return bytesLeft;
        }
Exemple #25
0
        private static int PushArgumentPointers(Process proc, UserPtr[] arr, ref UserPtr stack_top)
        {
            if (PushInt(proc, 0, ref stack_top) != 0)
                return -1;

            for (int i = arr.Length - 1; i >= 0; --i)
            {
                var p = arr[i];
                if (PushInt(proc, p.Value.ToInt32(), ref stack_top) != 0)
                    return -1;
            }
            return 0;
        }
Exemple #26
0
        private static int MapInSegments(File file, Process proc, ELF32Header eh)
        {
            Contract.Requires(file != null && file.GhostOwner == proc);
            Contract.Requires(proc.Space.GhostOwner == proc);

            var buf = new byte[ELF32ProgramHeader.Size];

            var ph = new ELF32ProgramHeader();
            // At this point we need to map in all stuff in PT_LOAD
            for (var i = 0; i < eh.NumOfProgramHeader; ++i)
            {
                var pos = (uint)(eh.ProgramHeaderOffest + eh.ProgramHeaderSize * i);
                if (file.Read(buf, ref pos) != buf.Length)
                    return -ErrorCode.EINVAL;

                ph = ELF32ProgramHeader.Read(buf);

                var size = ph.FileSize > ph.MemorySize ? ph.FileSize : ph.MemorySize;

                if (ph.type != ELF32ProgramHeader.PT_LOAD)
                    continue;

                // Round address to page boundary

                var diff = Arch.ArchDefinition.PageOffset(ph.vaddr);
                var vaddr = new Pointer(ph.vaddr);
                var offset = ph.offset;
                var memSize = (int)Arch.ArchDefinition.PageAlign((uint)ph.MemorySize);
                var fileSize = ph.FileSize;

                if (diff < 0 || ph.offset < diff || fileSize + diff > file.inode.Size || fileSize <= 0 || memSize <= 0)
                    return -ErrorCode.EINVAL;

                vaddr -= diff;
                offset -= (uint)diff;
                fileSize += diff;

                if (fileSize > memSize)
                    fileSize = memSize;

                if (proc.Space.AddMapping(ph.ExpressOSAccessFlag, 0, file, offset, fileSize, vaddr, memSize) != 0)
                    return -ErrorCode.ENOMEM;

                // Update brk

                var segmentEnd = (vaddr + memSize).ToUInt32();
                if (segmentEnd > proc.Space.Brk)
                {
                    proc.Space.InitializeBrk(segmentEnd);
                }
            }
            return 0;
        }
Exemple #27
0
 private static UserPtr[] PushCharArray(Process proc, ASCIIString[] arr, ref UserPtr stack_top)
 {
     var res = new UserPtr[arr.Length];
     for (int i = 0; i < arr.Length; ++i)
     {
         // Include the terminator
         stack_top -= arr[i].Length + 1;
         res[i] = stack_top;
         if (stack_top.Write(proc, arr[i].GetByteString()) != 0)
             return null;
     }
     return res;
 }
Exemple #28
0
        private static int PushInt(Process proc, int v, ref UserPtr stack_top)
        {
            stack_top -= sizeof(int);
            if (stack_top.Write(proc, v) != 0)
                return -1;

            return 0;
        }
Exemple #29
0
        public static void HandlePageFault(Process process, uint faultType, Pointer faultAddress, Pointer faultIP, out Pointer physicalPage, out uint permission)
        {
            // Object invariants of Process
            Contract.Assume(process.Space.GhostOwner == process);
            Contract.Assume(process.Space.Head.GhostOwner == process);

            // Never map page 0
            if (faultAddress.ToUInt32() < Arch.ArchDefinition.PageSize)
            {
                physicalPage = Pointer.Zero;
                permission = MemoryRegion.FALUT_NONE;
                return;
            }

            SyscallProfiler.EnterPageFault();
            var space = process.Space;
            var region = space.Find(faultAddress);

            if (region != null && (faultType & region.Access) != 0)
            {
                /*
                 * Check whether the kernel has allocated a page for this address,
                 * which might be the case due to UserPtr.Read() / UserPtr.Write().
                 */
                var mapped_in_page = space.UserToVirt(new UserPtr(faultAddress));

                if (mapped_in_page != Pointer.Zero)
                {
                    physicalPage = PageIndex(mapped_in_page);
                    permission = region.Access & MemoryRegion.FAULT_MASK;
                    return;
                }

                // If the page is a shared page from Linux, we'll need to ask linux to grab the page
                // Otherwise let's just allocate a fresh one.

                var shared_memory_region = IsAlienSharedRegion(region);
                var ghost_page_from_fresh_memory = false;

                ByteBufferRef buf;
                if (shared_memory_region)
                {
                    buf = Globals.LinuxMemoryAllocator.GetUserPage(process, faultType, ToShadowProcessAddress(faultAddress, region));
                    if (!buf.isValid)
                    {
                        Arch.Console.WriteLine("pager: cannot map in alien page.");
                        space.DumpAll();
                        physicalPage = Pointer.Zero;
                        permission = MemoryRegion.FALUT_NONE;
                        return;
                    }

                }
                else
                {
                    buf = Globals.PageAllocator.AllocPage();

                    ghost_page_from_fresh_memory = true;

                    if (!buf.isValid)
                    {
                        Arch.Console.WriteLine("Cannot allocate new pages");
                        Utils.Panic();
                    }

                    if (region.BackingFile != null)
                    {
                        var rel_pos = (PageIndex(faultAddress) - region.StartAddress);
                        uint pos = (uint)((ulong)rel_pos + region.FileOffset);

                        var readSizeLong = region.FileSize - rel_pos;
                        if (readSizeLong < 0)
                            readSizeLong = 0;
                        else if (readSizeLong > Arch.ArchDefinition.PageSize)
                            readSizeLong = Arch.ArchDefinition.PageSize;

                        int readSize = (int)readSizeLong;

                        Contract.Assert(region.BackingFile.GhostOwner == process);

                        var r = region.BackingFile.Read(buf, 0, readSize, ref pos);
                        if (r < 0)
                            r = 0;

                        Utils.Assert(r <= Arch.ArchDefinition.PageSize);

                        if (r < Arch.ArchDefinition.PageSize)
                            buf.ClearAfter(r);
                    }
                    else
                    {
                        buf.Clear();
                    }
                }

                Contract.Assert(shared_memory_region ^ ghost_page_from_fresh_memory);

                var page = new Pointer(buf.Location);
                space.AddIntoWorkingSet(new UserPtr(PageIndex(faultAddress)), page);

                SyscallProfiler.ExitPageFault();
                physicalPage = page;
                permission = region.Access & MemoryRegion.FAULT_MASK;
                return;
            }
            else
            {
                /*
                 * TODO: mmap2 enables the application requests an automatically expandable
                 * region (e.g., a stack)
                 *
                 * The feature doesn't seem to be actively used by the applications, since
                 * both the C runtime and the pthread library initializes stack explicitly.
                 *
                 * The feature is currently unimplemented.
                 */
            }

            physicalPage = Pointer.Zero;
            permission = MemoryRegion.FALUT_NONE;
            return;
        }