Add extended shared memory attach structure

This provides an extended version of `X_ShmAttachFd`, called
`X_ShmAttachFdExt`, which allows the client to specify two additional
parameters:

1. The size of the mapping, in bytes.
2. The offset that should be passed to `mmap`, in bytes.

The intended use-case for this extension is when one needs to map
a drawable from a character special device, but it can also be
used with regular files if one needs to map with a nonzero offset.
Qubes OS needs the X server to map the `/dev/xen/gntdev` character
device, which represents memory shared from a different virtual
machine.  This is currently accomplished with an `LD_PRELOAD`’d
shared library that overrides `shmat`, `shmctl`, and `shmdt`, and
I have written a version that instead overrides `fstat` and `mmap`.
With this extension, the preloaded shared library will not be required.
This commit is contained in:
Demi Marie Obenour 2021-07-20 19:26:09 -04:00 committed by Demi Marie Obenour
parent 2700bc6045
commit ed7b2ec457

View file

@ -1142,13 +1142,56 @@ ShmBusfaultNotify(void *context)
FreeResource (shmdesc->resource, RT_NONE);
}
static int
ProcShmAttachFdCommon(ClientPtr client, off_t offset, size_t size, int fd)
{
REQUEST(xShmAttachFdReq);
/* Parameters are validated already, so no need to check them */
ShmDescPtr shmdesc = malloc(sizeof(ShmDescRec));
if (!shmdesc) {
close(fd);
return BadAlloc;
}
shmdesc->is_fd = TRUE;
shmdesc->addr = mmap(NULL, size,
stuff->readOnly ? PROT_READ : PROT_READ|PROT_WRITE,
MAP_SHARED,
fd, offset);
close(fd);
if (shmdesc->addr == ((char *) -1)) {
free(shmdesc);
return BadAccess;
}
shmdesc->refcnt = 1;
shmdesc->writable = !stuff->readOnly;
shmdesc->size = size;
shmdesc->resource = stuff->shmseg;
shmdesc->busfault = busfault_register_mmap(shmdesc->addr, shmdesc->size, ShmBusfaultNotify, shmdesc);
if (!shmdesc->busfault) {
munmap(shmdesc->addr, shmdesc->size);
free(shmdesc);
return BadAlloc;
}
shmdesc->next = Shmsegs;
Shmsegs = shmdesc;
if (!AddResource(stuff->shmseg, ShmSegType, (void *) shmdesc))
return BadAlloc;
return Success;
}
static int
ProcShmAttachFd(ClientPtr client)
{
int fd;
ShmDescPtr shmdesc;
REQUEST(xShmAttachFdReq);
struct stat statb;
size_t size;
REQUEST(xShmAttachFdReq);
SetReqFds(client, 1);
REQUEST_SIZE_MATCH(xShmAttachFdReq);
@ -1166,41 +1209,58 @@ ProcShmAttachFd(ClientPtr client)
return BadMatch;
}
shmdesc = malloc(sizeof(ShmDescRec));
if (!shmdesc) {
size = (size_t)statb.st_size;
if ((off_t)size != statb.st_size) {
close(fd);
return BadAlloc;
}
shmdesc->is_fd = TRUE;
shmdesc->addr = mmap(NULL, statb.st_size,
stuff->readOnly ? PROT_READ : PROT_READ|PROT_WRITE,
MAP_SHARED,
fd, 0);
close(fd);
if (shmdesc->addr == ((char *) -1)) {
free(shmdesc);
return BadAccess;
return BadMatch;
}
shmdesc->refcnt = 1;
shmdesc->writable = !stuff->readOnly;
shmdesc->size = statb.st_size;
shmdesc->resource = stuff->shmseg;
return ProcShmAttachFdCommon(client, 0, size, fd);
}
shmdesc->busfault = busfault_register_mmap(shmdesc->addr, shmdesc->size, ShmBusfaultNotify, shmdesc);
if (!shmdesc->busfault) {
munmap(shmdesc->addr, shmdesc->size);
free(shmdesc);
return BadAlloc;
static int
ProcShmAttachFdExt(ClientPtr client)
{
int fd;
size_t size;
off_t offset;
REQUEST(xShmAttachFdExtReq);
SetReqFds(client, 1);
REQUEST_SIZE_MATCH(xShmAttachFdExtReq);
LEGAL_NEW_RESOURCE(stuff->shmseg, client);
if ((stuff->readOnly != xTrue) && (stuff->readOnly != xFalse)) {
client->errorValue = stuff->readOnly;
return BadValue;
}
fd = ReadFdFromClient(client);
if (fd < 0)
return BadMatch;
if (stuff->pad0) {
client->errorValue = stuff->pad0;
return BadValue;
}
shmdesc->next = Shmsegs;
Shmsegs = shmdesc;
if (stuff->pad1) {
client->errorValue = stuff->pad1;
return BadValue;
}
if (!AddResource(stuff->shmseg, ShmSegType, (void *) shmdesc))
return BadAlloc;
return Success;
size = (size_t)stuff->size;
offset = (off_t)stuff->offset;
if (size == 0 || (uint32_t)size != stuff->size) {
client->errorValue = stuff->size;
return BadValue;
}
if ((uint64_t)offset != stuff->offset) {
client->errorValue = stuff->offset;
return BadValue;
}
return ProcShmAttachFdCommon(client, offset, size, fd);
}
static int
@ -1369,6 +1429,8 @@ ProcShmDispatch(ClientPtr client)
return ProcShmAttachFd(client);
case X_ShmCreateSegment:
return ProcShmCreateSegment(client);
case X_ShmAttachFdExt:
return ProcShmAttachFdExt(client);
#endif
default:
return BadRequest;
@ -1492,6 +1554,19 @@ SProcShmCreateSegment(ClientPtr client)
swapl(&stuff->size);
return ProcShmCreateSegment(client);
}
static int _X_COLD
SProcShmAttachFdExt(ClientPtr client)
{
REQUEST(xShmAttachFdExtReq);
SetReqFds(client, 1);
swaps(&stuff->length);
REQUEST_SIZE_MATCH(xShmAttachFdExtReq);
swapl(&stuff->shmseg);
swapl(&stuff->size);
swapll(&stuff->offset);
return ProcShmAttachFd(client);
}
#endif /* SHM_FD_PASSING */
static int _X_COLD
@ -1521,6 +1596,8 @@ SProcShmDispatch(ClientPtr client)
return SProcShmAttachFd(client);
case X_ShmCreateSegment:
return SProcShmCreateSegment(client);
case X_ShmAttachFdExt:
return SProcShmAttachFdExt(client);
#endif
default:
return BadRequest;