kernel/vm: restrict permission changes on shared file-mapped areas
a protection_max attribute is added in VMArea. a read-only opened file already can't be mapped shared read-write at the moment, but can later be changed to read-write with mprotect() or set_area_protection(). When creating the VMArea, the actual maximum protection is stored in the area, so that it can be checked when needed. this fixes a VM TODO. Change-Id: I33b144c192034eeb059f1dede5dbef5af947280d Reviewed-on: https://review.haiku-os.org/c/haiku/+/3804 Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
committed by
Adrien Destugues
parent
99806fe3d6
commit
45872f7f9c
@@ -100,6 +100,7 @@ public:
|
|||||||
area_id id;
|
area_id id;
|
||||||
char name[B_OS_NAME_LENGTH];
|
char name[B_OS_NAME_LENGTH];
|
||||||
uint32 protection;
|
uint32 protection;
|
||||||
|
uint32 protection_max;
|
||||||
uint16 wiring;
|
uint16 wiring;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ static area_id sNextAreaID = 1;
|
|||||||
VMArea::VMArea(VMAddressSpace* addressSpace, uint32 wiring, uint32 protection)
|
VMArea::VMArea(VMAddressSpace* addressSpace, uint32 wiring, uint32 protection)
|
||||||
:
|
:
|
||||||
protection(protection),
|
protection(protection),
|
||||||
|
protection_max(0),
|
||||||
wiring(wiring),
|
wiring(wiring),
|
||||||
memory_type(0),
|
memory_type(0),
|
||||||
cache(NULL),
|
cache(NULL),
|
||||||
|
|||||||
+42
-20
@@ -274,7 +274,7 @@ static status_t vm_soft_fault(VMAddressSpace* addressSpace, addr_t address,
|
|||||||
bool isWrite, bool isExecute, bool isUser, vm_page** wirePage);
|
bool isWrite, bool isExecute, bool isUser, vm_page** wirePage);
|
||||||
static status_t map_backing_store(VMAddressSpace* addressSpace,
|
static status_t map_backing_store(VMAddressSpace* addressSpace,
|
||||||
VMCache* cache, off_t offset, const char* areaName, addr_t size, int wiring,
|
VMCache* cache, off_t offset, const char* areaName, addr_t size, int wiring,
|
||||||
int protection, int mapping, uint32 flags,
|
int protection, int protectionMax, int mapping, uint32 flags,
|
||||||
const virtual_address_restrictions* addressRestrictions, bool kernel,
|
const virtual_address_restrictions* addressRestrictions, bool kernel,
|
||||||
VMArea** _area, void** _virtualAddress);
|
VMArea** _area, void** _virtualAddress);
|
||||||
static void fix_protection(uint32* protection);
|
static void fix_protection(uint32* protection);
|
||||||
@@ -773,7 +773,7 @@ cut_area(VMAddressSpace* addressSpace, VMArea* area, addr_t address,
|
|||||||
// Map the second area.
|
// Map the second area.
|
||||||
error = map_backing_store(addressSpace, secondCache,
|
error = map_backing_store(addressSpace, secondCache,
|
||||||
area->cache_offset, area->name, secondSize, area->wiring,
|
area->cache_offset, area->name, secondSize, area->wiring,
|
||||||
area->protection, REGION_NO_PRIVATE_MAP, 0,
|
area->protection, area->protection_max, REGION_NO_PRIVATE_MAP, 0,
|
||||||
&addressRestrictions, kernel, &secondArea, NULL);
|
&addressRestrictions, kernel, &secondArea, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -807,8 +807,8 @@ cut_area(VMAddressSpace* addressSpace, VMArea* area, addr_t address,
|
|||||||
error = map_backing_store(addressSpace, cache, area->cache_offset
|
error = map_backing_store(addressSpace, cache, area->cache_offset
|
||||||
+ (secondBase - area->Base()),
|
+ (secondBase - area->Base()),
|
||||||
area->name, secondSize, area->wiring, area->protection,
|
area->name, secondSize, area->wiring, area->protection,
|
||||||
REGION_NO_PRIVATE_MAP, 0, &addressRestrictions, kernel, &secondArea,
|
area->protection_max, REGION_NO_PRIVATE_MAP, 0,
|
||||||
NULL);
|
&addressRestrictions, kernel, &secondArea, NULL);
|
||||||
if (error != B_OK) {
|
if (error != B_OK) {
|
||||||
addressSpace->ShrinkAreaTail(area, oldSize, allocationFlags);
|
addressSpace->ShrinkAreaTail(area, oldSize, allocationFlags);
|
||||||
return error;
|
return error;
|
||||||
@@ -921,16 +921,17 @@ discard_address_range(VMAddressSpace* addressSpace, addr_t address, addr_t size,
|
|||||||
*/
|
*/
|
||||||
static status_t
|
static status_t
|
||||||
map_backing_store(VMAddressSpace* addressSpace, VMCache* cache, off_t offset,
|
map_backing_store(VMAddressSpace* addressSpace, VMCache* cache, off_t offset,
|
||||||
const char* areaName, addr_t size, int wiring, int protection, int mapping,
|
const char* areaName, addr_t size, int wiring, int protection,
|
||||||
|
int protectionMax, int mapping,
|
||||||
uint32 flags, const virtual_address_restrictions* addressRestrictions,
|
uint32 flags, const virtual_address_restrictions* addressRestrictions,
|
||||||
bool kernel, VMArea** _area, void** _virtualAddress)
|
bool kernel, VMArea** _area, void** _virtualAddress)
|
||||||
{
|
{
|
||||||
TRACE(("map_backing_store: aspace %p, cache %p, virtual %p, offset 0x%"
|
TRACE(("map_backing_store: aspace %p, cache %p, virtual %p, offset 0x%"
|
||||||
B_PRIx64 ", size %" B_PRIuADDR ", addressSpec %" B_PRIu32 ", wiring %d"
|
B_PRIx64 ", size %" B_PRIuADDR ", addressSpec %" B_PRIu32 ", wiring %d"
|
||||||
", protection %d, area %p, areaName '%s'\n", addressSpace, cache,
|
", protection %d, protectionMax %d, area %p, areaName '%s'\n",
|
||||||
addressRestrictions->address, offset, size,
|
addressSpace, cache, addressRestrictions->address, offset, size,
|
||||||
addressRestrictions->address_specification, wiring, protection,
|
addressRestrictions->address_specification, wiring, protection,
|
||||||
_area, areaName));
|
protectionMax, _area, areaName));
|
||||||
cache->AssertLocked();
|
cache->AssertLocked();
|
||||||
|
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
@@ -954,6 +955,8 @@ map_backing_store(VMAddressSpace* addressSpace, VMCache* cache, off_t offset,
|
|||||||
|
|
||||||
VMArea* area = addressSpace->CreateArea(areaName, wiring, protection,
|
VMArea* area = addressSpace->CreateArea(areaName, wiring, protection,
|
||||||
allocationFlags);
|
allocationFlags);
|
||||||
|
if (mapping != REGION_PRIVATE_MAP)
|
||||||
|
area->protection_max = protectionMax & B_USER_PROTECTION;
|
||||||
if (area == NULL)
|
if (area == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
@@ -1270,7 +1273,7 @@ vm_block_address_range(const char* name, void* address, addr_t size)
|
|||||||
addressRestrictions.address = address;
|
addressRestrictions.address = address;
|
||||||
addressRestrictions.address_specification = B_EXACT_ADDRESS;
|
addressRestrictions.address_specification = B_EXACT_ADDRESS;
|
||||||
status = map_backing_store(addressSpace, cache, 0, name, size,
|
status = map_backing_store(addressSpace, cache, 0, name, size,
|
||||||
B_ALREADY_WIRED, 0, REGION_NO_PRIVATE_MAP, 0, &addressRestrictions,
|
B_ALREADY_WIRED, 0, REGION_NO_PRIVATE_MAP, 0, 0, &addressRestrictions,
|
||||||
true, &area, NULL);
|
true, &area, NULL);
|
||||||
if (status != B_OK) {
|
if (status != B_OK) {
|
||||||
cache->ReleaseRefAndUnlock();
|
cache->ReleaseRefAndUnlock();
|
||||||
@@ -1540,8 +1543,8 @@ vm_create_anonymous_area(team_id team, const char *name, addr_t size,
|
|||||||
cache->Lock();
|
cache->Lock();
|
||||||
|
|
||||||
status = map_backing_store(addressSpace, cache, 0, name, size, wiring,
|
status = map_backing_store(addressSpace, cache, 0, name, size, wiring,
|
||||||
protection, REGION_NO_PRIVATE_MAP, flags, virtualAddressRestrictions,
|
protection, 0, REGION_NO_PRIVATE_MAP, flags,
|
||||||
kernel, &area, _address);
|
virtualAddressRestrictions, kernel, &area, _address);
|
||||||
|
|
||||||
if (status != B_OK) {
|
if (status != B_OK) {
|
||||||
cache->ReleaseRefAndUnlock();
|
cache->ReleaseRefAndUnlock();
|
||||||
@@ -1742,7 +1745,7 @@ vm_map_physical_memory(team_id team, const char* name, void** _address,
|
|||||||
addressRestrictions.address = *_address;
|
addressRestrictions.address = *_address;
|
||||||
addressRestrictions.address_specification = addressSpec & ~B_MTR_MASK;
|
addressRestrictions.address_specification = addressSpec & ~B_MTR_MASK;
|
||||||
status = map_backing_store(locker.AddressSpace(), cache, 0, name, size,
|
status = map_backing_store(locker.AddressSpace(), cache, 0, name, size,
|
||||||
B_FULL_LOCK, protection, REGION_NO_PRIVATE_MAP, 0, &addressRestrictions,
|
B_FULL_LOCK, protection, 0, REGION_NO_PRIVATE_MAP, 0, &addressRestrictions,
|
||||||
true, &area, _address);
|
true, &area, _address);
|
||||||
|
|
||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
@@ -1858,7 +1861,7 @@ vm_map_physical_memory_vecs(team_id team, const char* name, void** _address,
|
|||||||
addressRestrictions.address = *_address;
|
addressRestrictions.address = *_address;
|
||||||
addressRestrictions.address_specification = addressSpec & ~B_MTR_MASK;
|
addressRestrictions.address_specification = addressSpec & ~B_MTR_MASK;
|
||||||
result = map_backing_store(locker.AddressSpace(), cache, 0, name,
|
result = map_backing_store(locker.AddressSpace(), cache, 0, name,
|
||||||
size, B_FULL_LOCK, protection, REGION_NO_PRIVATE_MAP, 0,
|
size, B_FULL_LOCK, protection, 0, REGION_NO_PRIVATE_MAP, 0,
|
||||||
&addressRestrictions, true, &area, _address);
|
&addressRestrictions, true, &area, _address);
|
||||||
|
|
||||||
if (result != B_OK)
|
if (result != B_OK)
|
||||||
@@ -1943,7 +1946,8 @@ vm_create_null_area(team_id team, const char* name, void** address,
|
|||||||
addressRestrictions.address = *address;
|
addressRestrictions.address = *address;
|
||||||
addressRestrictions.address_specification = addressSpec;
|
addressRestrictions.address_specification = addressSpec;
|
||||||
status = map_backing_store(locker.AddressSpace(), cache, 0, name, size,
|
status = map_backing_store(locker.AddressSpace(), cache, 0, name, size,
|
||||||
B_LAZY_LOCK, B_KERNEL_READ_AREA, REGION_NO_PRIVATE_MAP, flags,
|
B_LAZY_LOCK, B_KERNEL_READ_AREA, B_KERNEL_READ_AREA,
|
||||||
|
REGION_NO_PRIVATE_MAP, flags,
|
||||||
&addressRestrictions, true, &area, address);
|
&addressRestrictions, true, &area, address);
|
||||||
|
|
||||||
if (status < B_OK) {
|
if (status < B_OK) {
|
||||||
@@ -2049,6 +2053,13 @@ _vm_map_file(team_id team, const char* name, void** _address,
|
|||||||
return EACCES;
|
return EACCES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32 protectionMax = 0;
|
||||||
|
if (mapping != REGION_PRIVATE_MAP) {
|
||||||
|
protectionMax = protection | B_READ_AREA;
|
||||||
|
if ((openMode & O_ACCMODE) == O_RDWR)
|
||||||
|
protectionMax |= B_WRITE_AREA;
|
||||||
|
}
|
||||||
|
|
||||||
// get the vnode for the object, this also grabs a ref to it
|
// get the vnode for the object, this also grabs a ref to it
|
||||||
struct vnode* vnode = NULL;
|
struct vnode* vnode = NULL;
|
||||||
status_t status = vfs_get_vnode_from_fd(fd, kernel, &vnode);
|
status_t status = vfs_get_vnode_from_fd(fd, kernel, &vnode);
|
||||||
@@ -2115,7 +2126,7 @@ _vm_map_file(team_id team, const char* name, void** _address,
|
|||||||
addressRestrictions.address = *_address;
|
addressRestrictions.address = *_address;
|
||||||
addressRestrictions.address_specification = addressSpec;
|
addressRestrictions.address_specification = addressSpec;
|
||||||
status = map_backing_store(locker.AddressSpace(), cache, offset, name, size,
|
status = map_backing_store(locker.AddressSpace(), cache, offset, name, size,
|
||||||
0, protection, mapping,
|
0, protection, protectionMax, mapping,
|
||||||
unmapAddressRange ? CREATE_AREA_UNMAP_ADDRESS_RANGE : 0,
|
unmapAddressRange ? CREATE_AREA_UNMAP_ADDRESS_RANGE : 0,
|
||||||
&addressRestrictions, kernel, &area, _address);
|
&addressRestrictions, kernel, &area, _address);
|
||||||
|
|
||||||
@@ -2257,7 +2268,8 @@ vm_clone_area(team_id team, const char* name, void** address,
|
|||||||
addressRestrictions.address_specification = addressSpec;
|
addressRestrictions.address_specification = addressSpec;
|
||||||
status = map_backing_store(targetAddressSpace, cache,
|
status = map_backing_store(targetAddressSpace, cache,
|
||||||
sourceArea->cache_offset, name, sourceArea->Size(),
|
sourceArea->cache_offset, name, sourceArea->Size(),
|
||||||
sourceArea->wiring, protection, mapping, 0, &addressRestrictions,
|
sourceArea->wiring, protection, sourceArea->protection_max,
|
||||||
|
mapping, 0, &addressRestrictions,
|
||||||
kernel, &newArea, address);
|
kernel, &newArea, address);
|
||||||
}
|
}
|
||||||
if (status == B_OK && mapping != REGION_PRIVATE_MAP) {
|
if (status == B_OK && mapping != REGION_PRIVATE_MAP) {
|
||||||
@@ -2652,6 +2664,7 @@ vm_copy_area(team_id team, const char* name, void** _address,
|
|||||||
addressRestrictions.address_specification = addressSpec;
|
addressRestrictions.address_specification = addressSpec;
|
||||||
status = map_backing_store(targetAddressSpace, cache, source->cache_offset,
|
status = map_backing_store(targetAddressSpace, cache, source->cache_offset,
|
||||||
name, source->Size(), source->wiring, source->protection,
|
name, source->Size(), source->wiring, source->protection,
|
||||||
|
source->protection_max,
|
||||||
sharedArea ? REGION_NO_PRIVATE_MAP : REGION_PRIVATE_MAP,
|
sharedArea ? REGION_NO_PRIVATE_MAP : REGION_PRIVATE_MAP,
|
||||||
writableCopy ? 0 : CREATE_AREA_DONT_COMMIT_MEMORY,
|
writableCopy ? 0 : CREATE_AREA_DONT_COMMIT_MEMORY,
|
||||||
&addressRestrictions, true, &target, _address);
|
&addressRestrictions, true, &target, _address);
|
||||||
@@ -2727,6 +2740,15 @@ vm_set_area_protection(team_id team, area_id areaID, uint32 newProtection,
|
|||||||
" (%s)\n", team, newProtection, areaID, area->name);
|
" (%s)\n", team, newProtection, areaID, area->name);
|
||||||
return B_NOT_ALLOWED;
|
return B_NOT_ALLOWED;
|
||||||
}
|
}
|
||||||
|
if (!kernel && area->protection_max != 0
|
||||||
|
&& (newProtection & area->protection_max)
|
||||||
|
!= (newProtection & B_USER_PROTECTION)) {
|
||||||
|
dprintf("vm_set_area_protection: team %" B_PRId32 " tried to "
|
||||||
|
"set protection %#" B_PRIx32 " (max %#" B_PRIx32 ") on kernel "
|
||||||
|
"area %" B_PRId32 " (%s)\n", team, newProtection,
|
||||||
|
area->protection_max, areaID, area->name);
|
||||||
|
return B_NOT_ALLOWED;
|
||||||
|
}
|
||||||
|
|
||||||
if (area->protection == newProtection)
|
if (area->protection == newProtection)
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -6627,10 +6649,10 @@ _user_set_memory_protection(void* _address, size_t size, uint32 protection)
|
|||||||
|
|
||||||
if ((area->protection & B_KERNEL_AREA) != 0)
|
if ((area->protection & B_KERNEL_AREA) != 0)
|
||||||
return B_NOT_ALLOWED;
|
return B_NOT_ALLOWED;
|
||||||
|
if (area->protection_max != 0
|
||||||
// TODO: For (shared) mapped files we should check whether the new
|
&& (protection & area->protection_max) != protection) {
|
||||||
// protections are compatible with the file permissions. We don't
|
return B_NOT_ALLOWED;
|
||||||
// have a way to do that yet, though.
|
}
|
||||||
|
|
||||||
addr_t offset = currentAddress - area->Base();
|
addr_t offset = currentAddress - area->Base();
|
||||||
size_t rangeSize = min_c(area->Size() - offset, sizeLeft);
|
size_t rangeSize = min_c(area->Size() - offset, sizeLeft);
|
||||||
|
|||||||
Reference in New Issue
Block a user