Index: lib/libc/sys/mmap.2 =================================================================== --- lib/libc/sys/mmap.2 +++ lib/libc/sys/mmap.2 @@ -28,7 +28,7 @@ .\" @(#)mmap.2 8.4 (Berkeley) 5/11/95 .\" $FreeBSD$ .\" -.Dd June 22, 2017 +.Dd June 13, 2019 .Dt MMAP 2 .Os .Sh NAME @@ -113,6 +113,22 @@ Pages may be executed. .El .Pp +In addition to these protection flags, +.Fx +provides the ability to set the maximum protection of a region allocated by +.Nm +and later altered by +.Xr mprotect 2 . +This is accomplished by +.Em or Ns 'ing +one or more +.Dv PROT_ +value wrapped in the +.Dv PROT_MAX() +macro into the +.Fa prot +argument. +.Pp The .Fa flags argument specifies the type of the mapped object, mapping options and Index: lib/libc/sys/mprotect.2 =================================================================== --- lib/libc/sys/mprotect.2 +++ lib/libc/sys/mprotect.2 @@ -28,7 +28,7 @@ .\" @(#)mprotect.2 8.1 (Berkeley) 6/9/93 .\" $FreeBSD$ .\" -.Dd August 3, 2016 +.Dd June 13, 2019 .Dt MPROTECT 2 .Os .Sh NAME @@ -65,6 +65,22 @@ .It Dv PROT_EXEC The pages can be executed. .El +.Pp +In addition to these protection flags, +.Fx +provides the ability to set the maximum protection of a region +(e.g., prevents +.Nm +from upgrading the permissions). +This is accomplished by +.Em or Ns 'ing +one or more +.Dv PROT_ +value wrapped in the +.Dv PROT_MAX() +macro into the +.Fa prot +argument. .Sh RETURN VALUES .Rv -std mprotect .Sh ERRORS Index: sys/sys/mman.h =================================================================== --- sys/sys/mman.h +++ sys/sys/mman.h @@ -55,6 +55,14 @@ #define PROT_READ 0x01 /* pages can be read */ #define PROT_WRITE 0x02 /* pages can be written */ #define PROT_EXEC 0x04 /* pages can be executed */ +#if __BSD_VISIBLE +#define _PROT_ALL (PROT_READ | PROT_WRITE | PROT_EXEC) +#define PROT_EXTRACT(prot) ((prot) & _PROT_ALL) + +#define _PROT_MAX_SHIFT 16 +#define PROT_MAX(prot) ((prot) << _PROT_MAX_SHIFT) +#define PROT_MAX_EXTRACT(prot) (((prot) >> _PROT_MAX_SHIFT) & _PROT_ALL) +#endif /* * Flags contain sharing type and options. Index: sys/vm/vm_mmap.c =================================================================== --- sys/vm/vm_mmap.c +++ sys/vm/vm_mmap.c @@ -103,6 +103,9 @@ static int mincore_mapped = 1; SYSCTL_INT(_vm, OID_AUTO, mincore_mapped, CTLFLAG_RWTUN, &mincore_mapped, 0, "mincore reports mappings, not residency"); +static int imply_prot_max = 0; +SYSCTL_INT(_vm, OID_AUTO, imply_prot_max, CTLFLAG_RWTUN, &mincore_mapped, 0, + "Imply maximum page permissions in mmap() when none are specified."); #ifdef MAP_32BIT #define MAP_32BIT_MAX_ADDR ((vm_offset_t)1 << 31) @@ -187,9 +190,20 @@ vm_offset_t addr; vm_size_t pageoff; vm_prot_t cap_maxprot; - int align, error; + int align, error, max_prot; cap_rights_t rights; + max_prot = PROT_MAX_EXTRACT(prot); + prot = PROT_MAX(prot); + /* + * Always honor PROT_MAX if set. If not, default to all + * permissions unless we're implying maximum permissions. + * + * XXX: should be tunable per process and ABI. + */ + if (max_prot == 0) + max_prot = imply_prot_max ? prot : _PROT_ALL; + vms = td->td_proc->p_vmspace; fp = NULL; AUDIT_ARG_FD(fd); @@ -332,7 +346,7 @@ * This relies on VM_PROT_* matching PROT_*. */ error = vm_mmap_object(&vms->vm_map, &addr, size, prot, - VM_PROT_ALL, flags, NULL, pos, FALSE, td); + max_prot, flags, NULL, pos, FALSE, td); } else { /* * Mapping file, get fp for validation and don't let the @@ -360,7 +374,7 @@ /* This relies on VM_PROT_* matching PROT_*. */ error = fo_mmap(fp, &vms->vm_map, &addr, size, prot, - cap_maxprot, flags, pos, td); + max_prot & cap_maxprot, flags, pos, td); } if (error == 0) @@ -591,8 +605,10 @@ { vm_offset_t addr; vm_size_t pageoff; + int vm_error, max_prot; addr = addr0; + max_prot = PROT_MAX_EXTRACT(prot); prot = (prot & VM_PROT_ALL); pageoff = (addr & PAGE_MASK); addr -= pageoff; @@ -607,8 +623,19 @@ if (addr + size < addr) return (EINVAL); - switch (vm_map_protect(&td->td_proc->p_vmspace->vm_map, addr, - addr + size, prot, FALSE)) { + if (max_prot != 0) { + if ((max_prot & prot) != prot) + return (EINVAL); + vm_error = vm_map_protect(&td->td_proc->p_vmspace->vm_map, + addr, addr + size, max_prot, TRUE); + if (vm_error != KERN_SUCCESS) + goto error_out; + } + vm_error = vm_map_protect(&td->td_proc->p_vmspace->vm_map, addr, + addr + size, prot, FALSE); + +error_out: + switch (vm_error) { case KERN_SUCCESS: return (0); case KERN_PROTECTION_FAILURE: