Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/uipc_shm.c
Show First 20 Lines • Show All 695 Lines • ▼ Show 20 Lines | #endif | ||||
} | } | ||||
} | } | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
int | int | ||||
kern_shm_open(struct thread *td, const char *userpath, int flags, mode_t mode, | kern_shm_open(struct thread *td, const char *userpath, int flags, mode_t mode, | ||||
struct filecaps *fcaps) | struct filecaps *fcaps, int initial_seals) | ||||
{ | { | ||||
struct filedesc *fdp; | struct filedesc *fdp; | ||||
struct shmfd *shmfd; | struct shmfd *shmfd; | ||||
struct file *fp; | struct file *fp; | ||||
char *path; | char *path; | ||||
const char *pr_path; | const char *pr_path; | ||||
void *rl_cookie; | |||||
size_t pr_pathlen; | size_t pr_pathlen; | ||||
Fnv32_t fnv; | Fnv32_t fnv; | ||||
mode_t cmode; | mode_t cmode; | ||||
int fd, error; | int fd, error; | ||||
#ifdef CAPABILITY_MODE | #ifdef CAPABILITY_MODE | ||||
/* | /* | ||||
* shm_open(2) is only allowed for anonymous objects. | * shm_open(2) is only allowed for anonymous objects. | ||||
*/ | */ | ||||
if (IN_CAPABILITY_MODE(td) && (userpath != SHM_ANON)) | if (IN_CAPABILITY_MODE(td) && (userpath != SHM_ANON)) | ||||
return (ECAPMODE); | return (ECAPMODE); | ||||
#endif | #endif | ||||
AUDIT_ARG_FFLAGS(flags); | AUDIT_ARG_FFLAGS(flags); | ||||
AUDIT_ARG_MODE(mode); | AUDIT_ARG_MODE(mode); | ||||
if ((flags & O_ACCMODE) != O_RDONLY && (flags & O_ACCMODE) != O_RDWR) | if ((flags & O_ACCMODE) != O_RDONLY && (flags & O_ACCMODE) != O_RDWR) | ||||
return (EINVAL); | return (EINVAL); | ||||
if ((flags & ~(O_ACCMODE | O_CREAT | O_EXCL | O_TRUNC | O_CLOEXEC)) != 0) | if ((flags & ~(O_ACCMODE | O_CREAT | O_EXCL | O_TRUNC | O_CLOEXEC)) != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
/* | |||||
* Currently only F_SEAL_SEAL may be set when creating or opening shmfd. | |||||
* If the decision is made later to allow additional seals, care must be | |||||
* taken below to ensure that the seals are properly set if the shmfd | |||||
* already existed -- this currently assumes that only F_SEAL_SEAL can | |||||
* be set and doesn't take further precautions to ensure the validity of | |||||
* the seals being added with respect to current mappings. | |||||
*/ | |||||
if ((initial_seals & ~F_SEAL_SEAL) != 0) | |||||
return (EINVAL); | |||||
fdp = td->td_proc->p_fd; | fdp = td->td_proc->p_fd; | ||||
cmode = (mode & ~fdp->fd_cmask) & ACCESSPERMS; | cmode = (mode & ~fdp->fd_cmask) & ACCESSPERMS; | ||||
/* | /* | ||||
* shm_open(2) created shm should always have O_CLOEXEC set, as mandated | * shm_open(2) created shm should always have O_CLOEXEC set, as mandated | ||||
* by POSIX. We allow it to be unset here so that an in-kernel | * by POSIX. We allow it to be unset here so that an in-kernel | ||||
* interface may be written as a thin layer around shm, optionally not | * interface may be written as a thin layer around shm, optionally not | ||||
* setting CLOEXEC. For shm_open(2), O_CLOEXEC is set unconditionally | * setting CLOEXEC. For shm_open(2), O_CLOEXEC is set unconditionally | ||||
* in sys_shm_open() to keep this implementation compliant. | * in sys_shm_open() to keep this implementation compliant. | ||||
*/ | */ | ||||
error = falloc_caps(td, &fp, &fd, flags & O_CLOEXEC, fcaps); | error = falloc_caps(td, &fp, &fd, flags & O_CLOEXEC, fcaps); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
/* A SHM_ANON path pointer creates an anonymous object. */ | /* A SHM_ANON path pointer creates an anonymous object. */ | ||||
if (userpath == SHM_ANON) { | if (userpath == SHM_ANON) { | ||||
/* A read-only anonymous object is pointless. */ | /* A read-only anonymous object is pointless. */ | ||||
if ((flags & O_ACCMODE) == O_RDONLY) { | if ((flags & O_ACCMODE) == O_RDONLY) { | ||||
fdclose(td, fp, fd); | fdclose(td, fp, fd); | ||||
fdrop(fp, td); | fdrop(fp, td); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
shmfd = shm_alloc(td->td_ucred, cmode); | shmfd = shm_alloc(td->td_ucred, cmode); | ||||
shmfd->shm_seals = initial_seals; | |||||
} else { | } else { | ||||
path = malloc(MAXPATHLEN, M_SHMFD, M_WAITOK); | path = malloc(MAXPATHLEN, M_SHMFD, M_WAITOK); | ||||
pr_path = td->td_ucred->cr_prison->pr_path; | pr_path = td->td_ucred->cr_prison->pr_path; | ||||
/* Construct a full pathname for jailed callers. */ | /* Construct a full pathname for jailed callers. */ | ||||
pr_pathlen = strcmp(pr_path, "/") == 0 ? 0 | pr_pathlen = strcmp(pr_path, "/") == 0 ? 0 | ||||
: strlcpy(path, pr_path, MAXPATHLEN); | : strlcpy(path, pr_path, MAXPATHLEN); | ||||
error = copyinstr(userpath, path + pr_pathlen, | error = copyinstr(userpath, path + pr_pathlen, | ||||
Show All 20 Lines | if (shmfd == NULL) { | ||||
/* Object does not yet exist, create it if requested. */ | /* Object does not yet exist, create it if requested. */ | ||||
if (flags & O_CREAT) { | if (flags & O_CREAT) { | ||||
#ifdef MAC | #ifdef MAC | ||||
error = mac_posixshm_check_create(td->td_ucred, | error = mac_posixshm_check_create(td->td_ucred, | ||||
path); | path); | ||||
if (error == 0) { | if (error == 0) { | ||||
#endif | #endif | ||||
shmfd = shm_alloc(td->td_ucred, cmode); | shmfd = shm_alloc(td->td_ucred, cmode); | ||||
shmfd->shm_seals = initial_seals; | |||||
shm_insert(path, fnv, shmfd); | shm_insert(path, fnv, shmfd); | ||||
#ifdef MAC | #ifdef MAC | ||||
} | } | ||||
#endif | #endif | ||||
} else { | } else { | ||||
free(path, M_SHMFD); | free(path, M_SHMFD); | ||||
error = ENOENT; | error = ENOENT; | ||||
} | } | ||||
} else { | } else { | ||||
rl_cookie = rangelock_wlock(&shmfd->shm_rl, 0, OFF_MAX, | |||||
&shmfd->shm_mtx); | |||||
/* | /* | ||||
* kern_shm_open() likely shouldn't ever error out on | |||||
* trying to set a seal that already exists, unlike | |||||
* F_ADD_SEALS. This would break terribly as | |||||
* shm_open(2) actually sets F_SEAL_SEAL to maintain | |||||
* historical behavior where the underlying file could | |||||
* not be sealed. | |||||
*/ | |||||
initial_seals &= ~shmfd->shm_seals; | |||||
/* | |||||
* Object already exists, obtain a new | * Object already exists, obtain a new | ||||
* reference if requested and permitted. | * reference if requested and permitted. | ||||
*/ | */ | ||||
free(path, M_SHMFD); | free(path, M_SHMFD); | ||||
if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) | |||||
/* | |||||
* initial_seals can't set additional seals if we've | |||||
* already been set F_SEAL_SEAL. If F_SEAL_SEAL is set, | |||||
* then we've already removed that one from | |||||
* initial_seals. This is currently redundant as we | |||||
* only allow setting F_SEAL_SEAL at creation time, but | |||||
* it's cheap to check and decreases the effort required | |||||
* to allow additional seals. | |||||
*/ | |||||
if ((shmfd->shm_seals & F_SEAL_SEAL) != 0 && | |||||
initial_seals != 0) | |||||
error = EPERM; | |||||
else if ((flags & (O_CREAT | O_EXCL)) == | |||||
(O_CREAT | O_EXCL)) | |||||
error = EEXIST; | error = EEXIST; | ||||
else { | else { | ||||
#ifdef MAC | #ifdef MAC | ||||
error = mac_posixshm_check_open(td->td_ucred, | error = mac_posixshm_check_open(td->td_ucred, | ||||
shmfd, FFLAGS(flags & O_ACCMODE)); | shmfd, FFLAGS(flags & O_ACCMODE)); | ||||
if (error == 0) | if (error == 0) | ||||
#endif | #endif | ||||
error = shm_access(shmfd, td->td_ucred, | error = shm_access(shmfd, td->td_ucred, | ||||
FFLAGS(flags & O_ACCMODE)); | FFLAGS(flags & O_ACCMODE)); | ||||
} | } | ||||
/* | /* | ||||
* Truncate the file back to zero length if | * Truncate the file back to zero length if | ||||
* O_TRUNC was specified and the object was | * O_TRUNC was specified and the object was | ||||
* opened with read/write. | * opened with read/write. | ||||
*/ | */ | ||||
if (error == 0 && | if (error == 0 && | ||||
(flags & (O_ACCMODE | O_TRUNC)) == | (flags & (O_ACCMODE | O_TRUNC)) == | ||||
(O_RDWR | O_TRUNC)) { | (O_RDWR | O_TRUNC)) { | ||||
VM_OBJECT_WLOCK(shmfd->shm_object); | |||||
#ifdef MAC | #ifdef MAC | ||||
error = mac_posixshm_check_truncate( | error = mac_posixshm_check_truncate( | ||||
td->td_ucred, fp->f_cred, shmfd); | td->td_ucred, fp->f_cred, shmfd); | ||||
if (error == 0) | if (error == 0) | ||||
#endif | #endif | ||||
shm_dotruncate(shmfd, 0); | error = shm_dotruncate_locked(shmfd, 0, | ||||
rl_cookie); | |||||
VM_OBJECT_WUNLOCK(shmfd->shm_object); | |||||
} | } | ||||
if (error == 0) | if (error == 0) { | ||||
/* | |||||
* Currently we only allow F_SEAL_SEAL to be | |||||
* set initially. As noted above, this would | |||||
* need to be reworked should that change. | |||||
*/ | |||||
shmfd->shm_seals |= initial_seals; | |||||
shm_hold(shmfd); | shm_hold(shmfd); | ||||
} | } | ||||
rangelock_unlock(&shmfd->shm_rl, rl_cookie, | |||||
&shmfd->shm_mtx); | |||||
} | |||||
sx_xunlock(&shm_dict_lock); | sx_xunlock(&shm_dict_lock); | ||||
if (error) { | if (error) { | ||||
fdclose(td, fp, fd); | fdclose(td, fp, fd); | ||||
fdrop(fp, td); | fdrop(fp, td); | ||||
return (error); | return (error); | ||||
} | } | ||||
} | } | ||||
finit(fp, FFLAGS(flags & O_ACCMODE), DTYPE_SHM, shmfd, &shm_ops); | finit(fp, FFLAGS(flags & O_ACCMODE), DTYPE_SHM, shmfd, &shm_ops); | ||||
td->td_retval[0] = fd; | td->td_retval[0] = fd; | ||||
fdrop(fp, td); | fdrop(fp, td); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* System calls. */ | /* System calls. */ | ||||
int | int | ||||
sys_shm_open(struct thread *td, struct shm_open_args *uap) | sys_shm_open(struct thread *td, struct shm_open_args *uap) | ||||
{ | { | ||||
return (kern_shm_open(td, uap->path, uap->flags | O_CLOEXEC, uap->mode, | return (kern_shm_open(td, uap->path, uap->flags | O_CLOEXEC, uap->mode, | ||||
NULL)); | NULL, F_SEAL_SEAL)); | ||||
} | } | ||||
int | int | ||||
sys_shm_unlink(struct thread *td, struct shm_unlink_args *uap) | sys_shm_unlink(struct thread *td, struct shm_unlink_args *uap) | ||||
{ | { | ||||
char *path; | char *path; | ||||
const char *pr_path; | const char *pr_path; | ||||
size_t pr_pathlen; | size_t pr_pathlen; | ||||
▲ Show 20 Lines • Show All 398 Lines • Show Last 20 Lines |