int NaClVmmapChangeProt(struct NaClVmmap *self, uintptr_t page_num, size_t npages, int prot) { size_t i; size_t nvalid; uintptr_t new_region_end_page = page_num + npages; /* * NaClVmmapCheckExistingMapping should be always called before * NaClVmmapChangeProt proceeds to ensure that valid mapping exists * as modifications cannot be rolled back. */ if (!NaClVmmapCheckExistingMapping(self, page_num, npages, prot)) { return 0; } NaClLog(2, ("NaClVmmapChangeProt(0x%08"NACL_PRIxPTR", 0x%"NACL_PRIxPTR ", 0x%"NACL_PRIxS", 0x%x)\n"), (uintptr_t) self, page_num, npages, prot); NaClVmmapMakeSorted(self); /* * This loop & interval boundary tests closely follow those in * NaClVmmapUpdate. When updating those, do not forget to update them * at both places where appropriate. * TODO(phosek): use better data structure which will support intervals */ for (i = 0, nvalid = self->nvalid; i < nvalid && npages > 0; i++) { struct NaClVmmapEntry *ent = self->vmentry[i]; uintptr_t ent_end_page = ent->page_num + ent->npages; nacl_off64_t additional_offset = (new_region_end_page - ent->page_num) << NACL_PAGESHIFT; if (ent->page_num < page_num && new_region_end_page < ent_end_page) { /* Split existing mapping into two parts */ NaClVmmapAdd(self, new_region_end_page, ent_end_page - new_region_end_page, ent->prot, ent->flags, ent->desc, ent->offset + additional_offset, ent->file_size); ent->npages = page_num - ent->page_num; /* Add the new mapping into the middle. */ NaClVmmapAdd(self, page_num, npages, prot, ent->flags, ent->desc, ent->offset + (page_num - ent->page_num), ent->file_size); break; } else if (ent->page_num < page_num && page_num < ent_end_page) { /* New mapping overlaps end of existing mapping. */ ent->npages = page_num - ent->page_num; /* Add the overlapping part of the mapping. */ NaClVmmapAdd(self, page_num, ent_end_page - page_num, prot, ent->flags, ent->desc, ent->offset + (page_num - ent->page_num), ent->file_size); /* The remaining part (if any) will be added in other iteration. */ page_num = ent_end_page; npages = new_region_end_page - ent_end_page; } else if (ent->page_num < new_region_end_page && new_region_end_page < ent_end_page) { /* New mapping overlaps start of existing mapping, split it. */ NaClVmmapAdd(self, page_num, npages, prot, ent->flags, ent->desc, ent->offset, ent->file_size); ent->page_num = new_region_end_page; ent->npages = ent_end_page - new_region_end_page; ent->offset += additional_offset; break; } else if (page_num <= ent->page_num && ent_end_page <= new_region_end_page) { /* New mapping covers all of the existing mapping. */ page_num = ent_end_page; npages = new_region_end_page - ent_end_page; ent->prot = prot; } else { /* No overlap */ assert(new_region_end_page <= ent->page_num || ent_end_page <= page_num); } } return 1; }
/* * Update the virtual memory map. Deletion is handled by a remove * flag, since a NULL nmop just means that the memory is backed by the * system paging file. */ void NaClVmmapUpdate(struct NaClVmmap *self, uintptr_t page_num, size_t npages, int prot, struct NaClMemObj *nmop, int remove) { /* update existing entries or create new entry as needed */ size_t i; uintptr_t new_region_end_page = page_num + npages; NaClLog(2, ("NaClVmmapUpdate(0x%08"NACL_PRIxPTR", " "0x%"NACL_PRIxPTR", 0x%"NACL_PRIxS", " "0x%x, 0x%08"NACL_PRIxPTR", %d)\n"), (uintptr_t) self, page_num, npages, prot, (uintptr_t) nmop, remove); NaClVmmapMakeSorted(self); CHECK(npages > 0); for (i = 0; i < self->nvalid; i++) { struct NaClVmmapEntry *ent = self->vmentry[i]; uintptr_t ent_end_page = ent->page_num + ent->npages; nacl_off64_t additional_offset = (new_region_end_page - ent->page_num) << NACL_PAGESHIFT; if (ent->page_num < page_num && new_region_end_page < ent_end_page) { /* * Split existing mapping into two parts, with new mapping in * the middle. */ if (!NaClVmmapAdd(self, new_region_end_page, ent_end_page - new_region_end_page, ent->prot, NaClMemObjSplit(ent->nmop, additional_offset))) { NaClLog(LOG_FATAL, "NaClVmmapUpdate: could not split entry\n"); } ent->npages = page_num - ent->page_num; break; } else if (ent->page_num < page_num && page_num < ent_end_page) { /* New mapping overlaps end of existing mapping. */ ent->npages = page_num - ent->page_num; } else if (ent->page_num < new_region_end_page && new_region_end_page < ent_end_page) { /* New mapping overlaps start of existing mapping. */ NaClMemObjIncOffset(ent->nmop, additional_offset); ent->page_num = new_region_end_page; ent->npages = ent_end_page - new_region_end_page; break; } else if (page_num <= ent->page_num && ent_end_page <= new_region_end_page) { /* New mapping covers all of the existing mapping. */ ent->removed = 1; } else { /* No overlap */ assert(new_region_end_page <= ent->page_num || ent_end_page <= page_num); } } if (!remove) { if (!NaClVmmapAdd(self, page_num, npages, prot, nmop)) { NaClLog(LOG_FATAL, "NaClVmmapUpdate: could not add entry\n"); } } NaClVmmapRemoveMarked(self); }
/* * Update the virtual memory map. Deletion is handled by a remove * flag, since a NULL desc just means that the memory is backed by the * system paging file. */ static void NaClVmmapUpdate(struct NaClVmmap *self, uintptr_t page_num, size_t npages, int prot, int flags, int remove, struct NaClDesc *desc, nacl_off64_t offset, nacl_off64_t file_size) { /* update existing entries or create new entry as needed */ size_t i; uintptr_t new_region_end_page = page_num + npages; NaClLog(2, ("NaClVmmapUpdate(0x%08"NACL_PRIxPTR", 0x%"NACL_PRIxPTR", " "0x%"NACL_PRIxS", 0x%x, 0x%x, %d, 0x%"NACL_PRIxPTR", " "0x%"NACL_PRIx64")\n"), (uintptr_t) self, page_num, npages, prot, flags, remove, (uintptr_t) desc, offset); NaClVmmapMakeSorted(self); CHECK(npages > 0); for (i = 0; i < self->nvalid; i++) { struct NaClVmmapEntry *ent = self->vmentry[i]; uintptr_t ent_end_page = ent->page_num + ent->npages; nacl_off64_t additional_offset = (new_region_end_page - ent->page_num) << NACL_PAGESHIFT; if (ent->page_num < page_num && new_region_end_page < ent_end_page) { /* * Split existing mapping into two parts, with new mapping in * the middle. */ NaClVmmapAdd(self, new_region_end_page, ent_end_page - new_region_end_page, ent->prot, ent->flags, ent->desc, ent->offset + additional_offset, ent->file_size); ent->npages = page_num - ent->page_num; break; } else if (ent->page_num < page_num && page_num < ent_end_page) { /* New mapping overlaps end of existing mapping. */ ent->npages = page_num - ent->page_num; } else if (ent->page_num < new_region_end_page && new_region_end_page < ent_end_page) { /* New mapping overlaps start of existing mapping. */ ent->page_num = new_region_end_page; ent->npages = ent_end_page - new_region_end_page; ent->offset += additional_offset; break; } else if (page_num <= ent->page_num && ent_end_page <= new_region_end_page) { /* New mapping covers all of the existing mapping. */ ent->removed = 1; } else { /* No overlap */ assert(new_region_end_page <= ent->page_num || ent_end_page <= page_num); } } if (!remove) { NaClVmmapAdd(self, page_num, npages, prot, flags, desc, offset, file_size); } NaClVmmapRemoveMarked(self); }