shm_ctl(), shm_ctl_special()

Updated: April 19, 2023

Give special attributes to a shared memory object

Synopsis:

#include <sys/mman.h>

int shm_ctl( int fd,
             int flags,
             uint64_t paddr,
             uint64_t size );

int shm_ctl_special( int fd,
                     int flags,
                     uint64_t paddr,
                     uint64_t size,
                     unsigned special );

Arguments:

fd
The file descriptor that's associated with the shared memory object, as returned by shm_open().
flags
A bitwise OR of one or more SHMCTL_* flags, which are defined in <sys/mman.h>.
  • SHMCTL_ANON — allocate anonymous memory. If used with SHMCTL_PHYS (i.e., SHMCTL_ANON | SHMCTL_PHYS), the memory allocated is physically contiguous; SHMCTL_ANON without SHMCTL_PHYS requests mostly contiguous memory.
  • SHMCTL_GET_FLAGS (QNX Neutrino 7.1 or later) — return the flags defined for the object. If you specify this flag with other flags, the existing flags, not the new ones in the call, are returned.

    This flag is useful for learning if an object is assigned a physical address and hence, should not be given to unknown clients due to security risks, as explained in the paddr argument description below.

    Note: The flags returned don't include SHMCTL_TYMEM because it applies only to the backing that's installed when you call a specific instance of shm_ctl(). The object might be backed in part by memory not from typed memory.
  • SHMCTL_GLOBAL (deprecated) — hint that any mapping to the object could be global across all processes.
  • SHMCTL_HIGHUSAGE (deprecated) — hint that the object is used a lot, so the system should try to do things to speed up access to it.
  • SHMCTL_LAZY — delay allocating the memory until it's referenced. This flag implies SHMCTL_ANON.
  • SHMCTL_LOWERPROT (deprecated) — hint that the system may map this object in a way that trades lower memory protection for better performance.
  • SHMCTL_PHYS — If used without SHMCTL_ANON or SHMCTL_TYMEM; overlay the shared memory object onto the physical memory at the address given in paddr.
  • SHMCTL_PRIV (deprecated) — hint that a mapping of this object may require privileged access.
  • SHMCTL_REPEAT — create a strided physical object; that is, extend an existing object that was initialized with SHMCTL_PHYS, with a repeating pattern of physical addresses. For example:
              shm_ctl( fd, SHMCTL_PHYS, baseaddr, vstride );
              shm_ctl( fd, SHMCTL_REPEAT, pstride, count );
            
  • SHMCTL_REVOCABLE (QNX Neutrino 7.1 or later) — allow the creator of the shared memory object to revoke access to it by calling shm_revoke(). Only the creator of the object can set this flag; otherwise, shm_ctl() fails and sets errno to EPERM. After creating the object, you can either use this flag alone or paired with another flag that you can toggle (e.g. SHMCTL_SEAL).

    For more information, see Secure Buffer Management in the “Shared Memory” chapter of the QNX Neutrino Programmer's Guide.

  • SHMCTL_SEAL (QNX Neutrino 7.1 or later) — prevent the object's layout (e.g., its size and backing memory) and attributes from being modified. Only the process that created the object can set this flag; after it's set, no process (including the object's creator) can modify the layout or change any attributes. But, processes may still query the flags of an object with SHMCTL_GET_FLAGS.
    Note:
    • A sealed object is not write-protected. Anyone who has write access can still change the content of the object—they just can't shrink or extend it or otherwise make it point to memory not already referenced by it. The intent is to provide a safe way to use shared memory objects for interprocess communication.
    • If you want to seal a shared memory object after calling shm_ctl() or shm_ctl_special() to configure the object, you must use shm_ctl(). Calling shm_ctl_special() specifying only SHMCTL_SEAL fails with an error of EBADF.
  • SHMCTL_TYMEM (QNX Neutrino 7.1 or later) — allocate typed memory from the pool identified by a file descriptor that was returned by a call to posix_typed_mem_open(). It isn't necessary to keep this file descriptor open after the call to shm_ctl().

    If you specify SHMCTL_TYMEM, you must also specify SHMCTL_ANON. If and only if you specified the POSIX_TYPED_MEM_ALLOCATE_CONTIG flag for posix_typed_mem_open(), you must specify SHMCTL_PHYS in addition to SHMCTL_ANON and SHMCTL_TYMEM.

Note: Some of the bits have specific meanings for different processors. For more information, see:
paddr
The interpretation of this argument depends on the bits set in flags:
  • SHMCTL_PHYS, except when used with SHMCTL_TYMEM (see below) or with SHMCTL_ANON — a physical address to assign to the object. When SHMCTL_ANON | SHMCTL_PHYS is set, paddr should be 0, because any physical address pointed to by this argument will be ignored.
    CAUTION:
    Sharing memory through the direct mapping of physical addresses is extremely dangerous, and you should avoid it.

    For example, suppose process A allocates some memory, maps it, determines the physical address, and gives the address to process B so that it can map the memory. Process B uses direct mapping. Process A can free the memory or terminate, and the memory could then be given to another process, while process B continues to use it. This could cause a process or even the kernel to crash later.

  • SHMCTL_REPEAT — the stride, which is used to increment the physical address given to the original shm_ctl() call.
  • SHMCTL_TYMEM — a file descriptor that was returned by a call to posix_typed_mem_open(), cast to be a uint64_t.

If paddr is a physical address or a stride, its value must be a multiple of the page size (sysconf(_SC_PAGESIZE)).

size
The new size of the object, in bytes, regardless of the ANON/PHYS flag. If you specify SHMCTL_PHYS in the flags, then size must be a multiple of the page size.

For SHMCTL_REPEAT, this argument is the length, which is used to increment the offset within the object. The value must be a multiple of the page size.

special
(shm_ctl_special() only) Processor-specific flags; see the following:
Note: Calling shm_ctl_special() with a special argument of 0 is not equivalent to calling shm_ctl(). Setting special to 0 can clear some special bits that are on by default.

Library:

libc

Use the -l c option to qcc to link against this library. This library is usually included automatically.

Description:

The shm_ctl() function modifies the attributes of the shared memory object identified by the handle, fd. This handle is the value returned by shm_open(). The shm_ctl_special() function is similar to shm_ctl(), but has an additional, processor-specific argument.

Your process might need some abilities enabled (see procmgr_ability()):
In order to use: You need:
SHMCTL_GLOBAL PROCMGR_AID_MEM_GLOBAL
SHMCTL_PHYS PROCMGR_AID_MEM_PHYS
shm_ctl_special() PROCMGR_AID_MEM_SPECIAL

Typically, you can call shm_ctl() only once on a new object created with shm_open(). Reopening an existing object with the O_TRUNC flag doesn't make the object new again.

You can make subsequent calls to shm_ctl() only in order to extend an existing object, and only when using the same combination of SHMCTL_ANON and SHMCTL_PHYS as in the original call. How you can extend an existing object depends on these other two flags:
  • You can extend objects created with SHMCTL_PHYS (a physical address) in one of these ways:
    • by seeking beyond the end of the object—typically with lseek()—and installing another physical address and size with shm_ctl(... SHMCTL_PHYS ...)
    • by calling shm_ctl(... SHMCTL_REPEAT ...) when the file descriptor is at the beginning of the object
  • Objects created with SHMCTL_ANON or SHMCTL_LAZY are extended to a new size that's the current offset plus the size provided in the shm_ctl() call. Note that you can't shrink an object; the second call is ignored if the resulting size is smaller than the current one. For the non-lazy case, memory is allocated to fill the difference between the previous and current sizes.

If you first initialized an object with shm_ctl_special(), all subsequent calls that follow the rules above must also be done with shm_ctl_special(), using the same special attributes.

Shared memory objects that are populated with shm_ctl() are implicitly locked, unless you use the SHMCTL_LAZY flag.

Bits in the flags argument for x86 processors

For x86 processors:

Bits in the special argument for ARM processors

For ARM platforms, the special argument specifies the memory attributes that will be used when the object is mapped.

The type of mapping defines the default memory attributes assigned:
  • Any non-RAM memory locations, that are mapped using mmap() with the PROT_NOCACHE protection type, are assigned device-nGnRnE as a default (ARM_SHMCTL_SO).
  • Any RAM memory locations, that are mapped using mmap() with the PROT_NOCACHE protection type, are assigned Normal Uncached memory as a default (ARM_SHMCTL_NC).
  • Cached memory locations mapped using mmap() are assigned Normal Write-back, Write-Allocate as a default (ARM_SHMCTL_WBWA).
Note: (QNX Neutrino 7.1 or later) On AArch64 the ARM_SHMCTL_* bits are supported only for backward compatibility. New code should use the AARCH64_PTE_* values for attributes and shareability, ORed with the AARCH64_SHMCTL flag. See Bits in the special argument for AArch64 processors,” below.
You can specify the memory attributes via one of the following:
ARM_SHMCTL_SO
Map the object with the Strongly Ordered memory attribute (device-nGnRnE). This provides uncached access and provides implicit memory ordering equivalent to a barrier before and after the memory access.

Non-RAM memory locations, that are mapped using mmap() with the PROT_NOCACHE protection type, are assigned as device-nGnRnE as a default.

All loads and stores must be aligned for the access size, otherwise a memory fault is triggered resulting in delivery of a SIGBUS signal.

ARM_SHMCTL_DEV
Map the object with the Device memory attribute (device-nGnRE). This provides uncached access and is subject to the ordering rules defined by the ARM Architecture Reference Manual.

Reads are performed using a single memory access with the size as specified by the load instruction.

Stores are performed using a single memory access with the size as specified by the load instruction.

An explicit memory barrier may be required after store operations to ensure visibility of the store to the memory system.

For an example on using this flag, see Sharing device memory below.

ARM_SHMCTL_NC
Map the object with the Normal Uncached memory attribute. This provides uncached access and is subject to the ordering rules defined by the ARM Architecture Reference Manual.

Loads and stores may be performed to unaligned addresses, except for those instructions that always require correct alignment.

Stores can be merged into a write buffer before being written to the memory system.

An explicit memory barrier may be required after store operations to ensure visibility of the store to the memory system.

ARM_SHMCTL_WT
Map the object with the Normal Write-Through attribute. This provides cacheable access, subject to the ordering rules for Normal memory types.

Loads and stores may be performed to unaligned addresses, except for those instructions that always require correct alignment.

Loads allocate a cache line and return data from the cache.

Stores don't cause allocation of a cache line. If a store hits in the cache, the cache is modified and the data is written to memory via a write buffer. If a store misses in the cache, the data is written to memory via a write buffer. Multiple stores may be merged in the write buffer before being written to the memory system.

ARM_SHMCTL_WB
Map the object with the Normal Write-Back attribute. This provides cacheable access, subject to the ordering rules for Normal memory types.

Loads and stores may be performed to unaligned addresses, except for those instructions that always require correct alignment.

Loads allocate a cache line and return data from the cache.

Stores don't cause allocation of a cache line. If a store hits in the cache, the cache is modified and the line is marked for write back to the memory system at a later time. If a store misses in the cache, the data is written to memory via a write buffer. Multiple stores may be merged in the write buffer before being written to the memory system.

ARM_SHMCTL_WBWA
Map the object with the Normal Write-Back, Write-Allocate attribute. This provides cacheable access, subject to the ordering rules for Normal memory types.

Loads and stores may be performed to unaligned addresses, except for those instructions that always require correct alignment.

Loads allocate a cache line and return data from the cache.

Stores allocate a cache line and modify the cache, marking the line for write back to the memory system at a later time.

You can specify the shareability of the mapping with the following:
ARM_SHMCTL_SH
Map the object with the Shareable memory attribute. The memory may be accessed by multiple observers (other processors or bus masters). This is used to indicate that the memory has coherency requirements. You'll almost always want to specify this flag.

For ARM_SHMCTL_NC, accesses are coherent with respect to loads and stores, and the additional coherency applies to load/store exclusive operations.

For ARM_SHMCTL_WT, ARM_SHMCTL_WB, and ARM_SHMCTL_WBWA, this indicates that hardware cache coherency is required.

The level at which this coherency applies is system dependent, and it may still require explicit cache maintenance to enforce coherency for memory accesses by other bus masters (e.g., DMA operations).

Note: The ARM MMU is now configured to use the TEX remap facility, which limits the range of memory attributes that can be used. Versions of QNX Neutrino before 7.0 didn't use TEX remap, and this allowed shm_ctl_special() to specify any attribute encoded via the TEX[2:0], C, and B bits. Any encoding using nonzero TEX[2:1] bits is no longer possible.

You can find full details of the ARM memory attributes and ordering requirements in section A3.5, “Memory types and attributes and the memory order model,” in the ARM Architecture Reference Manual (ARM DDI 0406).

Bits in the special argument for AArch64 processors

(QNX Neutrino 7.1 or later) For AArch64 platforms, the special argument specifies the memory and shareability attributes that will be used when the object is mapped.

Note: On AArch64 the ARM_SHMCTL_* bits are supported only for backward compatibility. New code should use the AARCH64_PTE_* values for attributes and shareability, ORed with the AARCH64_SHMCTL flag.

You must specify one of the following memory attributes:

AARCH64_PTE_DEV
Map the object with the Device memory attribute (device-nGnRE). This provides uncached access and is subject to the ordering rules defined by the ARM Architecture Reference Manual.

All loads and stores must be aligned for the access size, otherwise a memory fault is triggered resulting in delivery of a SIGBUS signal.

Reads are performed using a single memory access with the size as specified by the load instruction.

Stores are performed using a single memory access with the size as specified by the load instruction.

An explicit memory barrier may be required after store operations to ensure visibility of the store to the memory system.

AARCH64_PTE_NC
Map the object with the Normal Uncached memory attribute. This provides uncached access and is subject to the ordering rules defined by the ARM Architecture Reference Manual.

Loads and stores may be performed to unaligned addresses, except for those instructions that always require correct alignment.

Stores can be merged into a write buffer before being written to the memory system.

An explicit memory barrier may be required after store operations to ensure visibility of the store to the memory system.

AARCH64_PTE_SODEV
Map the object with the Strongly Ordered memory attribute (device-nGnRnE). This provides uncached access and provides implicit memory ordering equivalent to a barrier before and after the memory access.

Non-RAM memory locations, that are mapped using mmap() with the PROT_NOCACHE protection type, are assigned as device-nGnRnE as a default.

All loads and stores must be aligned for the access size, otherwise a memory fault is triggered resulting in delivery of a SIGBUS signal.

AARCH64_PTE_WBnWA
Map the object with the Write-Back, No-Write-Allocate attribute.
AARCH64_PTE_WBWA
Map the object with the Normal Write-Back, Write-Allocate attribute. This provides cacheable access, subject to the ordering rules for Normal memory types.

Loads and stores may be performed to unaligned addresses, except for those instructions that always require correct alignment.

Loads allocate a cache line and return data from the cache.

Stores allocate a cache line and modify the cache, marking the line for write back to the memory system at a later time.

AARCH64_PTE_WTnWA
Map the object with the Write-Through, No-Write-Allocate attribute.

You can use a bitwise OR to include one of the following shareability attributes:

AARCH64_PTE_ISH
Inner shareable.
AARCH64_PTE_NSH
Non-shareable (the default).
AARCH64_PTE_OSH
Outer shareable.

If you don't specify inner- or outer-shareable, the memory is non-shareable (AARCH64_PTE_NSH). This is almost never what you want for non-device memory (device memory is outer-shareable). Unless you have a reason not to, you should specify inner-shareable for non-device memory.

For more information, see the ARM Architecture Reference Manual.

Returns:

0
Success.
>0
Success. If SHMCTL_GET_FLAGS is used, the function returns a bitfield of the flags that have been set by previous calls to shm_ctl() on the shared memory object.
-1
An error occurred (errno is set).

Errors:

EAGAIN
(shm_ctl_special() only) The special value doesn't match the current special value.
EBADF
One of the following occurred:
  • The shared memory object is already “special.”
  • After calling shm_ctl() or shm_ctl_special() to configure a shared memory object, you tried to seal the object by calling shm_ctl_special() specifying only SHMCTL_SEAL
EBUSY
You tried to extend a contiguous anonymous object (created with SHMCTL_ANON | SHMCTL_PHYS).
EINVAL
Invalid arguments, including the following:
  • An invalid combination of flags was specified.
  • You specified SHMCTL_PHYS in the flags, but the value of paddr or size isn't a multiple of the page size.
  • You tried to add segments inside an explicit physical object (created with SHMCTL_PHYS and a physical address).
  • You specified SHMCTL_REPEAT in the flags, and the stride isn't a multiple of the page size.
  • You specified SHMCTL_REVOCABLE in the flags, but the object wasn't populated with shm_ctl().
ENOMEM
There wasn't enough memory.
EPERM
One of the following occurred:
  • The calling process doesn't have the required permission; see procmgr_ability().
  • The creator specified SHMCTL_SEAL, so changes to the layout and flags are no longer permitted.
  • You specified SHMCTL_REVOCABLE in the flags, but you're not the creator of the object.

Examples:

Sharing device memory

This example shows how to create a region of device memory using a shared object, map this memory into the current process's address space, and then destroy the memory when the program is finished using it.
/*
 *  create_device_shm.c
*/
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <aarch64/mmu.h>
#include <stdint.h>
#include <assert.h>
#include <fcntl.h>

#define S1_SHM_NAME	"shm_s1"

#define TOTAL_PAGE_SIZE    (4096)

static char *src;
static int src_fd;

/** Remove the shared memory object and destroy the device memory */
void destroy_device_shm(int shm_fd, char *name)
{
    close(shm_fd);
    shm_unlink(name);
}

/**
 * Create a shared memory object and configure it to simulate device memory.
 * Return the file descriptor of the new object if successful, -1 otherwise.
 */
int create_device_shm(char *name, uint64_t size)
{
    int fd;
    int rc;

    fd = shm_open(name, O_RDWR|O_CREAT|O_TRUNC, 0666);
    if (fd < 0) {
        return -1;
    }
    rc = shm_ctl_special(fd, SHMCTL_ANON | SHMCTL_PHYS, 0, size, ARM_SHMCTL_DEV);
    if (rc != 0) {
        destroy_device_shm(fd, name);
        return -1;
    }

    return fd;
}

void cleanup()
{
    munmap(src, TOTAL_PAGE_SIZE);
    destroy_device_shm(src_fd, S1_SHM_NAME);
}

int main(void)
{
    // Create device memory region
    src_fd = create_device_shm(S1_SHM_NAME, TOTAL_PAGE_SIZE);
    assert(src_fd >= 0);

    // Map device memory into current address space
    src = mmap(NULL, TOTAL_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, src_fd, 0);
    assert(src != (void *)MAP_FAILED);

    // Your code goes here

    // Destroy device memory region
    cleanup();
    return 0;
}

Classification:

QNX Neutrino

Safety:  
Cancellation point Yes
Interrupt handler No
Signal handler Yes
Thread Yes