Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework shared memory in light of revocation and explicit mmap permissions #2225

Open
wants to merge 11 commits into
base: dev
Choose a base branch
from
6 changes: 2 additions & 4 deletions bin/cheribsdtest/cheribsdtest.c
Original file line number Diff line number Diff line change
Expand Up @@ -876,12 +876,10 @@ main(int argc, char *argv[])
* failure status.
*/
assert(sizeof(*ccsp) <= (size_t)getpagesize());
ccsp = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_ANON, -1,
0);
ccsp = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE | PROT_CAP,
MAP_ANON | MAP_SHARED, -1, 0);
if (ccsp == MAP_FAILED)
err(EX_OSERR, "mmap");
if (minherit(ccsp, getpagesize(), INHERIT_SHARE) < 0)
err(EX_OSERR, "minherit");

/*
* Disable core dumps unless specifically enabled.
Expand Down
9 changes: 7 additions & 2 deletions bin/cheribsdtest/cheribsdtest_cheriabi.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,11 +389,16 @@ CHERIBSDTEST(cheriabi_minherit_invalid_ptr,
CHERIBSDTEST_CHECK_CALL_ERROR(minherit(mappings.middle + mappings.maplen,
mappings.maplen, INHERIT_NONE), EPROT);

/*
* minherit() should not be able to mark a MAP_ANON mapping shared
* upless it was initially marked as shared.
*/
CHERIBSDTEST_CHECK_CALL_ERROR(minherit(mappings.middle, mappings.maplen,
INHERIT_SHARE), EACCES);

/* Sanity check: minherit() on a valid capability should succeed. */
CHERIBSDTEST_CHECK_SYSCALL(minherit(mappings.middle, mappings.maplen,
INHERIT_NONE));
CHERIBSDTEST_CHECK_SYSCALL(minherit(mappings.middle, mappings.maplen,
INHERIT_SHARE));

/* Unmapping the original capabilities should succeed. */
free_adjacent_mappings(&mappings);
Expand Down
5 changes: 3 additions & 2 deletions bin/cheribsdtest/cheribsdtest_ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,11 +217,12 @@ CHERIBSDTEST(ptrace_writecap, "Basic tests of PIOD_WRITE_CHERI_CAP")
uintcap_t *map, pp[2];
char capbuf[2][sizeof(uintcap_t) + 1];

fd = CHERIBSDTEST_CHECK_SYSCALL(shm_open(SHM_ANON, O_RDWR, 0600));
fd = CHERIBSDTEST_CHECK_SYSCALL(shm_open(SHM_ANON, O_RDWR | O_SHARECAP,
0600));
CHERIBSDTEST_CHECK_SYSCALL(ftruncate(fd, getpagesize()));

map = CHERIBSDTEST_CHECK_SYSCALL(mmap(NULL, getpagesize(),
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
PROT_READ | PROT_WRITE | PROT_CAP, MAP_SHARED, fd, 0));

pid = fork_child();

Expand Down
77 changes: 47 additions & 30 deletions bin/cheribsdtest/cheribsdtest_vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,20 +191,37 @@
}

CHERIBSDTEST(vm_tag_shm_open_anon_shared,
"check tags are stored for SHM_ANON MAP_SHARED pages")
"check tags are stored for SHM_ANON MAP_SHARED pages when requested")
{
int fd = CHERIBSDTEST_CHECK_SYSCALL(shm_open(SHM_ANON, O_RDWR, 0600));
CHERIBSDTEST_CHECK_SYSCALL(ftruncate(fd, getpagesize()));
mmap_and_check_tag_stored(fd, PROT_READ | PROT_WRITE, MAP_SHARED);
mmap_and_check_tag_stored(fd, PROT_READ | PROT_WRITE | PROT_CAP,
MAP_SHARED);
cheribsdtest_success();
}

CHERIBSDTEST(vm_tag_shm_open_anon_shared_implied_cap,
"check tags are not stored for SHM_ANON MAP_SHARED pages by default",
.ct_flags = CT_FLAG_SIGNAL | CT_FLAG_SI_CODE | CT_FLAG_SI_TRAPNO,
.ct_signum = SIGSEGV,
.ct_si_code = SEGV_STORETAG,
.ct_si_trapno = TRAPNO_STORE_CAP_PF,
.ct_check_skip = skip_need_writable_tmp)
{
int fd = CHERIBSDTEST_CHECK_SYSCALL(shm_open(SHM_ANON, O_RDWR, 0600));
CHERIBSDTEST_CHECK_SYSCALL(ftruncate(fd, getpagesize()));
mmap_and_check_tag_stored(fd, PROT_READ | PROT_WRITE, MAP_SHARED);

cheribsdtest_failure_errx("store succeeded");
}

CHERIBSDTEST(vm_tag_shm_open_anon_private,
"check tags are stored for SHM_ANON MAP_PRIVATE pages")
{
int fd = CHERIBSDTEST_CHECK_SYSCALL(shm_open(SHM_ANON, O_RDWR, 0600));
CHERIBSDTEST_CHECK_SYSCALL(ftruncate(fd, getpagesize()));
mmap_and_check_tag_stored(fd, PROT_READ | PROT_WRITE, MAP_PRIVATE);
mmap_and_check_tag_stored(fd, PROT_READ | PROT_WRITE | PROT_CAP,
MAP_PRIVATE);
cheribsdtest_success();
}

Expand All @@ -220,14 +237,15 @@
CHERIBSDTEST_CHECK_SYSCALL(ftruncate(fd, getpagesize()));

map2 = CHERIBSDTEST_CHECK_SYSCALL(mmap(NULL, getpagesize(),
PROT_READ, MAP_SHARED, fd, 0));
PROT_READ | PROT_CAP, MAP_SHARED, fd, 0));

/* Verify that no capability present */
c2 = *map2;
CHERIBSDTEST_VERIFY2(cheri_gettag(c2) == 0, "tag exists on first read");
CHERIBSDTEST_VERIFY2(c2 == NULL, "Initial read NULL");

mmap_and_check_tag_stored(fd, PROT_READ | PROT_WRITE, MAP_SHARED);
mmap_and_check_tag_stored(fd, PROT_READ | PROT_WRITE | PROT_CAP,
MAP_SHARED);

/* And now verify that it is, thanks to the aliased maps */
c2 = *map2;
Expand All @@ -237,10 +255,7 @@
cheribsdtest_success();
}

CHERIBSDTEST(vm_shm_open_anon_unix_surprise,
"test SHM_ANON vs SCM_RIGHTS",
.ct_xfail_reason =
"Tags currently survive cross-AS aliasing of SHM_ANON objects")
CHERIBSDTEST(vm_shm_open_anon_unix_cross_as, "test SHM_ANON vs SCM_RIGHTS")
brooksdavis marked this conversation as resolved.
Show resolved Hide resolved
{
int sv[2];
int pid;
Expand Down Expand Up @@ -280,14 +295,13 @@
CHERIBSDTEST_VERIFY2(fd >= 0, "fd read OK");

map = CHERIBSDTEST_CHECK_SYSCALL(mmap(NULL, getpagesize(),
PROT_READ, MAP_SHARED, fd, 0));
PROT_READ | PROT_CAP, MAP_SHARED, fd, 0));
c = *map;

if (verbose)
fprintf(stderr, "rx cap: %#lp\n", c);

tag = cheri_gettag(c);
CHERIBSDTEST_VERIFY2(tag == 0, "tag read");

CHERIBSDTEST_CHECK_SYSCALL(munmap(map, getpagesize()));
close(sv[0]);
Expand All @@ -309,12 +323,12 @@

close(sv[0]);

fd = CHERIBSDTEST_CHECK_SYSCALL(shm_open(SHM_ANON, O_RDWR, 0600));
fd = CHERIBSDTEST_CHECK_SYSCALL(shm_open(SHM_ANON,
O_RDWR | O_SHARECAP, 0600));
CHERIBSDTEST_CHECK_SYSCALL(ftruncate(fd, getpagesize()));

map = CHERIBSDTEST_CHECK_SYSCALL(mmap(NULL, getpagesize(),
PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0));
PROT_READ | PROT_WRITE | PROT_CAP, MAP_SHARED, fd, 0));

/* Just some pointer */
*map = &fd;
Expand Down Expand Up @@ -346,9 +360,11 @@

waitpid(pid, &res, 0);
if (res == 0) {
cheribsdtest_failure_errx("tags failed to transfer");
} else if (WIFEXITED(res) && WEXITSTATUS(res) == 1) {
cheribsdtest_success();
} else {
cheribsdtest_failure_errx("tag transfer succeeded");
cheribsdtest_failure_errx("child setup error occured (this is *unexpected*");

Check warning on line 367 in bin/cheribsdtest/cheribsdtest_vm.c

View workflow job for this annotation

GitHub Actions / Style Checker

line over 80 characters
}
}
}
Expand All @@ -365,7 +381,7 @@
CHERIBSDTEST_CHECK_SYSCALL(ftruncate(fd, getpagesize()));

map = CHERIBSDTEST_CHECK_SYSCALL(mmap(NULL, getpagesize(),
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
PROT_READ | PROT_WRITE | PROT_CAP, MAP_SHARED, fd, 0));

/* Just some pointer */
*map = &fd;
Expand Down Expand Up @@ -636,12 +652,11 @@
* and write a tagged capability to it.
*
* 2) Create a second copy-on-write mapping; read back the tagged value via
* the second mapping, and confirm that it still has a tag.
* (cheribsdtest_vm_cow_read)
* the second mapping, and confirm that it still has a tag. (vm_cow_read)
*
* 3) Write an adjacent word in the second mapping, which should cause a
* copy-on-write, then read back the capability and confirm that it still has
* a tag. (cheribsdtest_vm_cow_write)
* a tag. (vm_cow_write)
*/
CHERIBSDTEST(vm_cow_read,
"read capabilities from a copy-on-write page")
Expand All @@ -661,9 +676,11 @@
* Create 'real' and copy-on-write mappings.
*/
cp_real = CHERIBSDTEST_CHECK_SYSCALL2(mmap(NULL, getpagesize(),
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0), "mmap cp_real");
PROT_READ | PROT_WRITE | PROT_CAP, MAP_SHARED, fd, 0),
"mmap cp_real");
cp_copy = CHERIBSDTEST_CHECK_SYSCALL2(mmap(NULL, getpagesize(),
PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0), "mmap cp_copy");
PROT_READ | PROT_WRITE | PROT_CAP, MAP_PRIVATE, fd, 0),
"mmap cp_copy");

/*
* Write out a tagged capability to 'real' mapping -- doesn't really
Expand Down Expand Up @@ -710,9 +727,11 @@
* Create 'real' and copy-on-write mappings.
*/
cp_real = CHERIBSDTEST_CHECK_SYSCALL2(mmap(NULL, getpagesize(),
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0), "mmap cp_real");
PROT_READ | PROT_WRITE | PROT_CAP, MAP_SHARED, fd, 0),
"mmap cp_real");
cp_copy = CHERIBSDTEST_CHECK_SYSCALL2(mmap(NULL, getpagesize(),
PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0), "mmap cp_copy");
PROT_READ | PROT_WRITE | PROT_CAP, MAP_PRIVATE, fd, 0),
"mmap cp_copy");

/*
* Write out a tagged capability to 'real' mapping -- doesn't really
Expand Down Expand Up @@ -1248,7 +1267,7 @@
"psind=%d errno=%d", psind, errno);
CHERIBSDTEST_CHECK_SYSCALL(ftruncate(fd, ps[psind]));
addr = CHERIBSDTEST_CHECK_SYSCALL(mmap(NULL, ps[psind],
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
PROT_READ | PROT_WRITE | PROT_CAP, MAP_SHARED, fd, 0));

/* Verify mmap output */
CHERIBSDTEST_VERIFY2(cheri_gettag(addr) != 0,
Expand Down Expand Up @@ -2687,8 +2706,7 @@
}

CHERIBSDTEST(cheri_revoke_shm_anon_hoard_unmapped,
"Capability is revoked within an unmapped shm object",
.ct_xfail_reason = "unmapped part of shm objects aren't revoked")
"Capability is revoked within an unmapped shm object")
{
int fd;
void * volatile to_revoke;
Expand All @@ -2698,7 +2716,7 @@
CHERIBSDTEST_CHECK_SYSCALL(ftruncate(fd, getpagesize()));

map = CHERIBSDTEST_CHECK_SYSCALL(mmap(NULL, getpagesize(),
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
PROT_READ | PROT_WRITE | PROT_CAP, MAP_SHARED, fd, 0));

to_revoke = malloc(1);
*map = to_revoke;
Expand All @@ -2720,8 +2738,7 @@
}

CHERIBSDTEST(cheri_revoke_shm_anon_hoard_closed,
"Capability is revoked within an unmapped and closed shm object",
.ct_xfail_reason = "unmapped part of shm objects aren't revoked")
"Capability is revoked within an unmapped and closed shm object")
{
int sv[2];
int pid;
Expand Down Expand Up @@ -2784,7 +2801,7 @@
CHERIBSDTEST_CHECK_SYSCALL(ftruncate(fd, getpagesize()));

map = CHERIBSDTEST_CHECK_SYSCALL(mmap(NULL, getpagesize(),
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
PROT_READ | PROT_WRITE | PROT_CAP, MAP_SHARED, fd, 0));

to_revoke = malloc(1);
*map = to_revoke;
Expand Down
21 changes: 21 additions & 0 deletions lib/libsys/shm_open.2
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,27 @@ The size of the object can be adjusted via
and queried via
.Xr fstat 2 .
.Pp
On systems supporting CHERI capabilities, capabilities may only be shared
within a single address space unless the shared memory object is created
with the
.Dv O_SHARECAP
flag in the initial call to
.Fn shm_open .
Additionally, capabilities can only be stored to or loaded from mappings
created using
.Xr mmap 2
where the
.Dv PROT_CAP
permission is specified.
brooksdavis marked this conversation as resolved.
Show resolved Hide resolved
Without
.Dv PROT_CAP
stores of valid capabilities will trigger a fault and loads will always
yield an invalid capability.
On fork, pages with capability permissions remain shared, but are
downgraded to remove capability permissions unless
.Dv O_SHARECAP
was used during object creation.
.Pp
The new descriptor is set to close during
.Xr execve 2
system calls;
Expand Down
19 changes: 16 additions & 3 deletions lib/libsysdecode/flags.c
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,9 @@ sysdecode_fadvice(int advice)
return (lookup_value(fadvisebehav, advice));
}

bool
sysdecode_open_flags(FILE *fp, int flags, int *rem)
static bool
_sysdecode_open_flags(FILE *fp, int flags, int *rem,
struct name_table open_flag_table[])
{
bool printed;
int mode;
Expand Down Expand Up @@ -309,12 +310,24 @@ sysdecode_open_flags(FILE *fp, int flags, int *rem)
printed = false;
}
val = (unsigned)flags;
print_mask_part(fp, openflags, &val, &printed);
print_mask_part(fp, open_flag_table, &val, &printed);
if (rem != NULL)
*rem = val | mode;
return (printed);
}

bool
sysdecode_open_flags(FILE *fp, int flags, int *rem)
{
return (_sysdecode_open_flags(fp, flags, rem, openflags));
}

bool
sysdecode_shm_open_flags(FILE *fp, int flags, int *rem)
{
return (_sysdecode_open_flags(fp, flags, rem, shm_open_flags));
}

bool
sysdecode_fcntl_fileflags(FILE *fp, int flags, int *rem)
{
Expand Down
1 change: 1 addition & 0 deletions lib/libsysdecode/mktables
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ gen_table "rlimit" "RLIMIT_[A-Z]+[[:space:]]+[0-9]+" "sys/
gen_table "rusage" "RUSAGE_[A-Z]+[[:space:]]+[-0-9]+" "sys/resource.h"
gen_table "schedpolicy" "SCHED_[A-Z]+[[:space:]]+[0-9]+" "sys/sched.h"
gen_table "sendfileflags" "SF_[A-Z]+[[:space:]]+[0-9]+" "sys/socket.h"
gen_table "shm_open_flags" "O_ACCMODE|O_CREAT|O_EXCL|O_TRUNC|O_CLOEXEC|O_SHARECAP" "sys/fcntl.h"
gen_table "shmatflags" "SHM_[A-Z]+[[:space:]]+[0-9]{6}" "sys/shm.h"
gen_table "shutdownhow" "SHUT_[A-Z]+[[:space:]]+[0-9]+" "sys/socket.h"
gen_table "sigbuscode" "BUS_[A-Z]+[[:space:]]+[0-9]+" "sys/signal.h"
Expand Down
1 change: 1 addition & 0 deletions lib/libsysdecode/sysdecode.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ bool sysdecode_sctp_snd_flags(FILE *_fp, int _flags, int *_rem);
const char *sysdecode_semctl_cmd(int _cmd);
bool sysdecode_semget_flags(FILE *_fp, int _flag, int *_rem);
bool sysdecode_sendfile_flags(FILE *_fp, int _flags, int *_rem);
bool sysdecode_shm_open_flags(FILE *_fp, int _flags, int *_rem);
bool sysdecode_shmat_flags(FILE *_fp, int _flags, int *_rem);
const char *sysdecode_shmctl_cmd(int _cmd);
const char *sysdecode_shutdown_how(int _how);
Expand Down
4 changes: 4 additions & 0 deletions lib/libsysdecode/sysdecode_mask.3
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
.Nm sysdecode_rfork_flags ,
.Nm sysdecode_semget_flags ,
.Nm sysdecode_sendfile_flags ,
.Nm sysdecode_shm_open_flags ,
.Nm sysdecode_shmat_flags ,
.Nm sysdecode_sctp_nxt_flags ,
.Nm sysdecode_sctp_rcv_flags ,
Expand Down Expand Up @@ -119,6 +120,8 @@
.Ft bool
.Fn sysdecode_sendfile_flags "FILE *fp" "int flags" "int *rem"
.Ft bool
.Fn sysdecode_shm_open_flags "FILE *fp" "int flags" "int *rem"
.Ft bool
.Fn sysdecode_shmat_flags "FILE *fp" "int flags" "int *rem"
.Ft bool
.Fn sysdecode_socket_type "FILE *fp" "int type" "int *rem"
Expand Down Expand Up @@ -186,6 +189,7 @@ Most of these functions decode an argument passed to a system call:
.It Fn sysdecode_rfork_flags Ta Xr rfork 2 Ta Fa flags
.It Fn sysdecode_semget_flags Ta Xr semget 2 Ta Fa flags
.It Fn sysdecode_sendfile_flags Ta Xr sendfile 2 Ta Fa flags
.It Fn sysdecode_shm_open_flags Ta Xr shm_open 2 Ta Fa flags
.It Fn sysdecode_shmat_flags Ta Xr shmat 2 Ta Fa flags
.It Fn sysdecode_socket_type Ta Xr socket 2 Ta Fa type
.It Fn sysdecode_thr_create_flags Ta Xr thr_create 2 Ta Fa flags
Expand Down
1 change: 1 addition & 0 deletions sys/compat/linuxkpi/common/include/linux/list.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
#include <vm/vm.h>
#include <vm/vm_object.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#endif

#ifndef prefetch
Expand Down
Loading
Loading