Relax restrictions on private mappings of POSIX shm objects.

When creating a private mapping of a POSIX shared memory object,
VM_PROT_WRITE should always be included in maxprot regardless of
permissions on the underlying FD.  Otherwise it is possible to open a
shm object read-only, map it with MAP_PRIVATE and PROT_WRITE, and
violate the invariant in vm_map_insert() that (prot & maxprot) == prot.

Reported by:	syzkaller
Reviewed by:	kevans, kib
MFC after:	1 week
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D24398
This commit is contained in:
Mark Johnston 2020-04-13 19:20:39 +00:00
parent 605c4cda2f
commit c7841c6b8e

View File

@ -1136,23 +1136,28 @@ shm_mmap(struct file *fp, vm_map_t map, vm_offset_t *addr, vm_size_t objsize,
/*
* If FWRITE's set, we can allow VM_PROT_WRITE unless it's a shared
* mapping with a write seal applied.
* mapping with a write seal applied. Private mappings are always
* writeable.
*/
if ((fp->f_flag & FWRITE) != 0 && ((flags & MAP_SHARED) == 0 ||
(shmfd->shm_seals & F_SEAL_WRITE) == 0))
if ((flags & MAP_SHARED) == 0) {
cap_maxprot |= VM_PROT_WRITE;
maxprot |= VM_PROT_WRITE;
writecnt = false;
} else {
if ((fp->f_flag & FWRITE) != 0 &&
(shmfd->shm_seals & F_SEAL_WRITE) == 0)
maxprot |= VM_PROT_WRITE;
writecnt = (prot & VM_PROT_WRITE) != 0;
if (writecnt && (shmfd->shm_seals & F_SEAL_WRITE) != 0) {
error = EPERM;
goto out;
}
writecnt = (flags & MAP_SHARED) != 0 && (prot & VM_PROT_WRITE) != 0;
if (writecnt && (shmfd->shm_seals & F_SEAL_WRITE) != 0) {
error = EPERM;
goto out;
}
/* Don't permit shared writable mappings on read-only descriptors. */
if (writecnt && (maxprot & VM_PROT_WRITE) == 0) {
error = EACCES;
goto out;
/* Don't permit shared writable mappings on read-only descriptors. */
if (writecnt && (maxprot & VM_PROT_WRITE) == 0) {
error = EACCES;
goto out;
}
}
maxprot &= cap_maxprot;