feat(mman): mmap/munmap/mprotect/msync/mlock/madvise/shm_open

This commit is contained in:
2026-09-05 23:13:18 -04:00
parent f9a055f547
commit 3bf875030a
3 changed files with 796 additions and 0 deletions
+216
View File
@@ -0,0 +1,216 @@
#ifndef VLIBC_SYS_MMAN_H
#define VLIBC_SYS_MMAN_H
/*
* vlibc — <sys/mman.h>.
*
* Memory mapping, protection, and synchronization (POSIX.1-2008). Every
* function here is an unbuffered pass-through to the kernel: failures are
* reported as -1 (or MAP_FAILED for mmap) with errno set by the syscall
* layer.
*
* Level 1 (onlyposix): mmap, munmap, mprotect, msync, mlock, munlock,
* mlockall, munlockall, posix_madvise.
* Level 2 (muslmimic): madvise (XSI — posix_madvise is the POSIX base
* form), shm_open, shm_unlink.
*
* All constant values are Linux x86_64 kernel-UAPI facts (asm-generic/
* mman-common.h and mman.h), transcribed, not invented. PROT_*, MAP_*,
* MAP_FAILED, MS_* and MCL_* exist at level 1. The POSIX_MADV_* advice
* values match the Linux MADV_* values for the same advice (0-4), which is
* what lets posix_madvise pass its advice straight to SYS_madvise; the
* Linux MADV_* names themselves are gated at level 2 with madvise, the only
* function that takes them. MAP_ANONYMOUS is provided as a source
* compatibility alias of the primary spelling MAP_ANON (the BSD/POSIX
* draft name glibc also accepts).
*
* POSIX shared memory (shm_open/shm_unlink, level 2) is backed by a real
* named file under /dev/shm (tmpfs), NOT by memfd_create: the object is a
* path in the shared-memory filesystem, visible there and re-openable by
* name until shm_unlink removes it, which matches POSIX's named-object
* semantics. vlibc renders name as "/dev/shm/" + name and requires that
* name contain no '/' — the object is a single directory entry, so a slash
* would escape the namespace (EINVAL). This differs from the POSIX wording
* that a name begin with a slash: vlibc names carry no leading slash and
* the prefix is added internally. Otherwise shm_open behaves as open(2) on
* the rendered path (the oflag access modes plus O_CREAT/O_EXCL/O_TRUNC and
* mode bits are honored; O_WRONLY alone is undefined by POSIX and passed
* through to the kernel) and shm_unlink as unlink(2) on it.
*
* None of these declarations carry an intent attribute: every function
* changes the caller's address space or performs I/O with side effects and
* reports failures through errno, so const/pure would be unsound (the same
* rationale unistd.h documents for its I/O family).
*/
#include <vlibc/features.h>
#include <stddef.h>
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/* ---- memory protection ---- */
#define PROT_NONE 0x0 /* page cannot be accessed */
#define PROT_READ 0x1 /* page can be read */
#define PROT_WRITE 0x2 /* page can be written */
#define PROT_EXEC 0x4 /* page can be executed */
/* ---- mapping types and flags ---- */
#define MAP_SHARED 0x01 /* share changes with the file and other mappers */
#define MAP_PRIVATE 0x02 /* private copy-on-write mapping */
#define MAP_FIXED 0x10 /* interpret addr exactly; fail if it cannot be used */
#define MAP_ANON 0x20 /* anonymous mapping, not file-backed (primary name) */
#define MAP_ANONYMOUS MAP_ANON /* source-compatibility alias of MAP_ANON */
/* Value returned by mmap on failure (never a valid mapping address). */
#define MAP_FAILED ((void *)-1)
/* ---- msync synchronization flags ---- */
#define MS_ASYNC 0x1 /* return before the writes take effect */
#define MS_INVALIDATE 0x2 /* invalidate other mappings of the file */
#define MS_SYNC 0x4 /* perform synchronous writes */
/* ---- mlockall flags ---- */
#define MCL_CURRENT 0x1 /* lock all currently mapped pages */
#define MCL_FUTURE 0x2 /* lock all pages mapped in the future */
/* ---- posix_madvise advice (Linux MADV_* values, see the header note) ---- */
#define POSIX_MADV_NORMAL 0 /* no special treatment */
#define POSIX_MADV_RANDOM 1 /* pages will be accessed randomly */
#define POSIX_MADV_SEQUENTIAL 2 /* pages will be accessed sequentially */
#define POSIX_MADV_WILLNEED 3 /* pages will be needed soon */
#define POSIX_MADV_DONTNEED 4 /* pages are not needed soon */
/*
* Map len bytes starting at offset off of the object open on fildes into
* the process address space and return the mapping address, or MAP_FAILED
* with errno set. addr is a hint for the placement (0 for "anywhere")
* unless flags contains MAP_FIXED; prot is a combination of PROT_* (PROT_NONE
* alone forbids all access). With MAP_ANON fildes is ignored and must be -1
* and off 0; the mapping is zero-filled. The mapping is shared or private
* per MAP_SHARED/MAP_PRIVATE. len is rounded up to whole pages.
*/
void *
mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
/*
* Remove the mapping at addr (a page boundary) covering len bytes; return
* 0, or -1 with errno set. The address range becomes invalid.
*/
int
munmap(void *addr, size_t len);
/*
* Change the protection of the mapped pages at addr covering len bytes to
* prot; return 0, or -1 with errno set. addr must be page-aligned.
*/
int
mprotect(void *addr, size_t len, int prot);
/*
* Flush the mapped pages at addr covering len bytes of a MAP_SHARED mapping
* to (or invalidate them from) the underlying object per flags (MS_ASYNC,
* MS_SYNC, MS_INVALIDATE); return 0, or -1 with errno set.
*/
int
msync(void *addr, size_t len, int flags);
/*
* Lock the pages at addr covering len bytes into memory so they are never
* paged out; return 0, or -1 with errno set. munlock unlocks them again.
*/
int
mlock(const void *addr, size_t len);
int
munlock(const void *addr, size_t len);
/*
* Lock all pages mapped by the process into memory per flags (MCL_CURRENT,
* MCL_FUTURE); return 0, or -1 with errno set. munlockall unlocks them.
*/
int
mlockall(int flags);
int
munlockall(void);
/*
* Give the kernel advice (one of the POSIX_MADV_* values) about how the
* pages at addr covering len bytes will be used; return 0, or -1 with errno
* set. The advice may be ignored; the mapping is unchanged.
*/
int
posix_madvise(void *addr, size_t len, int advice);
#if VLIBC_LEVEL_GE(2)
/* Level 2 (muslmimic): XSI + POSIX shared memory. */
/* ---- madvise advice (Linux x86_64 kernel-UAPI values) ---- */
#define MADV_NORMAL 0 /* no special treatment */
#define MADV_RANDOM 1 /* pages will be accessed randomly */
#define MADV_SEQUENTIAL 2 /* pages will be accessed sequentially */
#define MADV_WILLNEED 3 /* pages will be needed soon */
#define MADV_DONTNEED 4 /* pages are not needed soon; free them */
#define MADV_FREE 8 /* free pages; contents lost on later write */
#define MADV_REMOVE 9 /* free the pages and punch a hole in the file */
#define MADV_DONTFORK 10 /* do not inherit the pages across fork */
#define MADV_DOFORK 11 /* revert MADV_DONTFORK */
#define MADV_MERGEABLE 12 /* enable KSM merging of the pages */
#define MADV_UNMERGEABLE 13 /* revert MADV_MERGEABLE */
#define MADV_HUGEPAGE 14 /* prefer transparent huge pages */
#define MADV_NOHUGEPAGE 15 /* revert MADV_HUGEPAGE */
#define MADV_DONTDUMP 16 /* exclude the pages from core dumps */
#define MADV_DODUMP 17 /* revert MADV_DONTDUMP */
#define MADV_WIPEONFORK 18 /* zero the pages in the child after fork */
#define MADV_KEEPONFORK 19 /* revert MADV_WIPEONFORK */
#define MADV_COLD 20 /* pages will be accessed less soon */
#define MADV_PAGEOUT 21 /* reclaim the pages immediately */
#define MADV_POPULATE_READ 22 /* fault the pages in for reading */
#define MADV_POPULATE_WRITE 23 /* fault the pages in for writing */
#define MADV_DONTNEED_LOCKED 24 /* like MADV_DONTNEED on locked pages */
#define MADV_COLLAPSE 25 /* collapse the range into a THP */
/*
* Linux madvise: give the kernel advice (one of the MADV_* values above)
* about the pages at addr covering len bytes; return 0, or -1 with errno
* set. XSI — posix_madvise (level 1) is the POSIX base form; this is the
* raw Linux interface exposing the full MADV_* set.
*/
int
madvise(void *addr, size_t len, int advice);
/*
* Open (creating with mode if oflag contains O_CREAT) the POSIX shared
* memory object name and return a descriptor, or -1 with errno set. The
* object is a file under /dev/shm; name must not contain '/', and the
* object persists (re-openable by name) until shm_unlink removes it. See
* the header note on the naming deviation. oflag behaves as for open(2).
*/
int
shm_open(const char *name, int oflag, mode_t mode);
/*
* Remove the shared memory object name from /dev/shm; return 0, or -1 with
* errno set. Open descriptors of the object stay valid until closed. See
* the header note on the naming deviation.
*/
int
shm_unlink(const char *name);
#endif /* VLIBC_LEVEL_GE(2) */
#ifdef __cplusplus
}
#endif
#endif /* VLIBC_SYS_MMAN_H */
+227
View File
@@ -0,0 +1,227 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdint.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "../internal/syscall.h"
/*
* The mmap family over the raw x86_64 syscalls. Every wrapper is a thin
* pass-through: SYS_mmap, SYS_munmap, SYS_mprotect, SYS_msync, SYS_mlock,
* SYS_munlock, SYS_mlockall, SYS_munlockall, SYS_madvise. Arguments are
* cast to long because that is the shape of the syscall register file (the
* wrappers in arch/x86_64/syscall_arch.h take long arguments).
*
* mmap is the one member that cannot use syscall_ret(): its success value
* is an address, which syscall_ret() would truncate to int. The kernel
* result is therefore checked against the -4095 MAX_ERRNO bound directly
* (the same test malloc.c applies to its private mmap) and the full pointer
* is preserved on success. The other members funnel their result through
* syscall_ret(), which returns the value on success and -1 with errno set
* on error.
*
* Level 2 adds madvise (the raw Linux interface behind posix_madvise) and
* the POSIX shared-memory pair. shm_open/shm_unlink live on real files
* under /dev/shm (see <sys/mman.h> for the naming rationale and the
* deviation from POSIX's leading-slash requirement): the object name is
* validated, rendered as "/dev/shm/" + name into a local buffer, and handed
* to SYS_openat / SYS_unlinkat directly — no inter-object dependency on
* the unistd wrappers, the same self-containment creat.c uses.
*/
/* Length of the "/dev/shm/" prefix (VLIBC_SHM_PREFIX_LEN bytes + NUL). */
#define VLIBC_SHM_PREFIX "/dev/shm/"
#define VLIBC_SHM_PREFIX_LEN (sizeof(VLIBC_SHM_PREFIX) - 1)
/* Longest object name: /dev/shm is a tmpfs, whose NAME_MAX is 255. */
#define VLIBC_SHM_NAME_MAX 255
#define VLIBC_SHM_PATH_MAX (VLIBC_SHM_PREFIX_LEN + VLIBC_SHM_NAME_MAX + 1)
/*
* Map len bytes starting at offset off of the file descriptor fildes into
* the process address space and return the mapping address. On failure
* MAP_FAILED is returned with errno set. len is rounded up to whole pages
* by the kernel; addr is a hint unless MAP_FIXED is given.
*/
void *
mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
{
long r = __syscall6(SYS_mmap, (long)addr, (long)len, prot, flags, fildes, (long)off);
if ((uintptr_t)r > (uintptr_t)-4096)
{
errno = (int)-r; /* the kernel returned -errno */
return MAP_FAILED;
}
return (void *)r;
}
/*
* Remove the mapping at addr covering len bytes; return 0, or -1 with errno
* set. addr must be page-aligned.
*/
int
munmap(void *addr, size_t len)
{
return syscall_ret(__syscall2(SYS_munmap, (long)addr, (long)len));
}
/*
* Change the protection of the pages at addr covering len bytes to prot;
* return 0, or -1 with errno set.
*/
int
mprotect(void *addr, size_t len, int prot)
{
return syscall_ret(__syscall3(SYS_mprotect, (long)addr, (long)len, prot));
}
/*
* Flush the pages at addr covering len bytes of a MAP_SHARED mapping to
* (or invalidate them from) the underlying object per flags; return 0, or
* -1 with errno set.
*/
int
msync(void *addr, size_t len, int flags)
{
return syscall_ret(__syscall3(SYS_msync, (long)addr, (long)len, flags));
}
/*
* Lock the pages at addr covering len bytes into memory so they are never
* paged out; return 0, or -1 with errno set. munlock undoes the lock.
*/
int
mlock(const void *addr, size_t len)
{
return syscall_ret(__syscall2(SYS_mlock, (long)addr, (long)len));
}
int
munlock(const void *addr, size_t len)
{
return syscall_ret(__syscall2(SYS_munlock, (long)addr, (long)len));
}
/*
* Lock all pages mapped by the process into memory per flags (MCL_CURRENT,
* MCL_FUTURE); return 0, or -1 with errno set. munlockall undoes the lock.
*/
int
mlockall(int flags)
{
return syscall_ret(__syscall1(SYS_mlockall, flags));
}
int
munlockall(void)
{
return syscall_ret(__syscall0(SYS_munlockall));
}
/*
* Give the kernel the POSIX_MADV_* advice about the pages at addr covering
* len bytes; return 0, or -1 with errno set. The advice values (0-4) match
* the Linux MADV_* values, so the call is a direct SYS_madvise.
*/
int
posix_madvise(void *addr, size_t len, int advice)
{
return syscall_ret(__syscall3(SYS_madvise, (long)addr, (long)len, advice));
}
#if VLIBC_LEVEL_GE(2)
/*
* Linux madvise: give the kernel the MADV_* advice about the pages at addr
* covering len bytes; return 0, or -1 with errno set. XSI — posix_madvise
* is the POSIX base form; this exposes the full Linux MADV_* set.
*/
int
madvise(void *addr, size_t len, int advice)
{
return syscall_ret(__syscall3(SYS_madvise, (long)addr, (long)len, advice));
}
/*
* Validate name (no empty names, no '/', tmpfs NAME_MAX bound) and render
* the /dev/shm path into buf (capacity VLIBC_SHM_PATH_MAX). Returns 0 and
* fills buf on success; -1 with errno set (EINVAL, ENAMETOOLONG) on error.
*/
static int
vlibc_shm_path(char *buf, const char *name)
{
size_t i;
size_t n;
for (n = 0; name[n] != '\0'; n++)
{
if (name[n] == '/')
{
errno = EINVAL;
return -1;
}
}
if (n == 0)
{
errno = EINVAL;
return -1;
}
if (n > VLIBC_SHM_NAME_MAX)
{
errno = ENAMETOOLONG;
return -1;
}
for (i = 0; i < VLIBC_SHM_PREFIX_LEN; i++)
{
buf[i] = VLIBC_SHM_PREFIX[i];
}
for (i = 0; i < n; i++)
{
buf[VLIBC_SHM_PREFIX_LEN + i] = name[i];
}
buf[VLIBC_SHM_PREFIX_LEN + n] = '\0';
return 0;
}
/*
* Open (creating with mode when oflag contains O_CREAT) the POSIX shared
* memory object name and return a descriptor, or -1 with errno set. The
* object is a real file under /dev/shm and persists until shm_unlink. See
* <sys/mman.h> on the naming deviation.
*/
int
shm_open(const char *name, int oflag, mode_t mode)
{
char path[VLIBC_SHM_PATH_MAX];
if (vlibc_shm_path(path, name) != 0)
{
return -1;
}
return syscall_ret(__syscall4(SYS_openat, AT_FDCWD, (long)path, oflag, (long)mode));
}
/*
* Remove the shared memory object name from /dev/shm; return 0, or -1 with
* errno set. Open descriptors of the object stay valid until closed.
*/
int
shm_unlink(const char *name)
{
char path[VLIBC_SHM_PATH_MAX];
if (vlibc_shm_path(path, name) != 0)
{
return -1;
}
return syscall_ret(__syscall3(SYS_unlinkat, AT_FDCWD, (long)path, 0));
}
#endif /* VLIBC_LEVEL_GE(2) */
+353
View File
@@ -0,0 +1,353 @@
/*
* vlibc — sys/mman.h test (task 33).
*
* Exercises the mmap family end to end over the raw syscall wrappers:
*
* 1. Anonymous mmap of one page → readable/writable, write + read-back
* round-trip, munmap releases it.
* 2. mmap PROT_NONE succeeds; mprotect to PROT_READ|PROT_WRITE makes the
* page writable (the write succeeds and reads back).
* 3. msync MS_SYNC on a MAP_SHARED file mapping flushes the pointer
* writes to the backing file: a read() of the same fd afterwards sees
* the data written through the mapping.
* 4. mlock/munlock of an anonymous page, and mlockall/munlockall,
* return 0.
* 5. posix_madvise (POSIX_MADV_DONTNEED) returns 0.
* 6. MAP_SHARED file mapping round-trip is proven by scenario 3 (write
* via the mapping, read back through the fd).
*
* Level-2 gated section: madvise(MADV_DONTNEED) returns 0, and POSIX
* shared memory round-trips: shm_open creates a /dev/shm object, a write
* survives close, a second shm_open (no O_CREAT) re-opens the same object
* and reads the data back (proving the real-file backing), and shm_unlink
* removes it.
*
* The -f mode runs the failure scenarios — mmap MAP_FIXED at an unaligned
* address → MAP_FAILED, munmap of that address → -1, shm_open of a name
* containing '/' → -1. These make the LIBRARY write errno (syscall_ret),
* which under a host-linked binary targets glibc's private dtv slot at
* %fs:0+8; the process therefore exits via raw SYS_exit_group without
* running host cleanup (house pattern, tests/test_unistd_file.c). In the
* default mode every exercised call succeeds, so no library errno write
* happens; the one host-TCB slot-1 save/restore at main brackets the whole
* run anyway (task 13 technique), keeping host state intact for the return
* through __libc_start_main. The test NEVER reads errno; every negative is
* asserted on the return value only.
*
* All diagnostics go through raw SYS_write (no stdio): under -Iinclude the
* vlibc public headers shadow GCC's internal ones, so a host header would
* not compile. Not part of the library proper; compiled manually for this
* todo (the tests/ + make check wiring is owned by a later todo).
*/
#include "../include/fcntl.h"
#include "../include/sys/mman.h"
#include "../include/unistd.h"
#include "../src/internal/syscall.h"
static int failures;
/* Write a NUL-terminated string to fd via the raw syscall layer. The
* optimize attribute keeps GCC from lowering the length loop into a
* strlen call, which would leave a vlibc-owned symbol undefined in this
* host-linked standalone binary (house idiom, see src/string). */
static __attribute__((optimize("no-tree-loop-distribute-patterns"))) void
say(int fd, const char *s)
{
long n = 0;
while (s[n] != '\0')
{
n++;
}
__syscall3(SYS_write, fd, (long)s, n);
}
static void
check(int cond, const char *what)
{
if (cond)
{
say(1, "PASS: ");
say(1, what);
say(1, "\n");
}
else
{
say(2, "FAIL: ");
say(2, what);
say(2, "\n");
failures++;
}
}
/*
* Host-TCB slot-1 bracket: the library's errno write on a negative path
* lands at %fs:0+8, glibc's dtv pointer. Save and restore it around the
* whole default-mode run; only vlibc/raw-syscall code runs in between
* (task 13 technique).
*/
static unsigned long
tcb_slot1(void)
{
return *(unsigned long *)((char *)__builtin_thread_pointer() + 8);
}
static void
tcb_slot1_set(unsigned long value)
{
*(unsigned long *)((char *)__builtin_thread_pointer() + 8) = value;
}
/* Fill buf with the repeating pattern i * 7 + 1 (unsigned char). */
static void
fill_pattern(unsigned char *buf, unsigned long n)
{
unsigned long i;
for (i = 0; i < n; i++)
{
buf[i] = (unsigned char)(i * 7 + 1);
}
}
/* 1+2: anonymous mappings, protection change. */
static void
anon_scenarios(void)
{
static unsigned char pattern[4096];
unsigned char *p;
unsigned long i;
int ok = 1;
fill_pattern(pattern, sizeof(pattern));
p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
check(p != MAP_FAILED && p != NULL, "mmap 4096 anonymous returns a mapping");
if (p != MAP_FAILED && p != NULL)
{
for (i = 0; i < 4096; i++)
{
p[i] = pattern[i];
}
for (i = 0; i < 4096; i++)
{
if (p[i] != pattern[i])
{
ok = 0;
}
}
check(ok, "write + read-back round-trips on the anonymous mapping");
check(munmap(p, 4096) == 0, "munmap of the anonymous mapping returns 0");
}
p = mmap(NULL, 4096, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
check(p != MAP_FAILED && p != NULL, "mmap 4096 PROT_NONE anonymous returns a mapping");
if (p != MAP_FAILED && p != NULL)
{
check(mprotect(p, 4096, PROT_READ | PROT_WRITE) == 0,
"mprotect(PROT_NONE -> PROT_READ|PROT_WRITE) returns 0");
p[0] = 'P';
p[1] = 'W';
check(p[0] == 'P' && p[1] == 'W', "the page is writable after mprotect");
check(munmap(p, 4096) == 0, "munmap of the mprotect page returns 0");
}
}
/* 3+6: msync + MAP_SHARED file visibility, plus the mlock family (4+5). */
static void
file_and_lock_scenarios(const char *path)
{
static unsigned char pattern[4096];
unsigned char *p;
unsigned char rbuf[16];
int fd;
fill_pattern(pattern, sizeof(pattern));
fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0600);
check(fd >= 0, "open creates the msync backing file");
if (fd < 0)
{
return;
}
check(write(fd, pattern, sizeof(pattern)) == (ssize_t)sizeof(pattern),
"write of 4096 bytes to the backing file returns 4096");
p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
check(p != MAP_FAILED && p != NULL, "mmap MAP_SHARED of the file returns a mapping");
if (p != MAP_FAILED && p != NULL)
{
p[0] = 'M';
p[1] = 'S';
p[2] = 'Y';
p[3] = 'N';
p[4] = 'C';
p[5] = '!';
check(msync(p, 4096, MS_SYNC) == 0, "msync MS_SYNC returns 0");
check(lseek(fd, 0, SEEK_SET) == 0, "lseek back to offset 0 returns 0");
check(read(fd, rbuf, sizeof(rbuf)) == (ssize_t)sizeof(rbuf),
"read of the backing file returns 16 bytes");
check(rbuf[0] == 'M' && rbuf[1] == 'S' && rbuf[2] == 'Y' && rbuf[3] == 'N' &&
rbuf[4] == 'C' && rbuf[5] == '!',
"the msync'd mapping writes are visible through the fd");
check(munmap(p, 4096) == 0, "munmap of the shared mapping returns 0");
}
check(close(fd) == 0, "close of the backing file returns 0");
{
unsigned char *q;
q = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
check(q != MAP_FAILED && q != NULL, "mmap a page for the mlock family");
if (q != MAP_FAILED && q != NULL)
{
q[0] = 'L';
check(mlock(q, 4096) == 0, "mlock of the page returns 0");
check(munlock(q, 4096) == 0, "munlock of the page returns 0");
check(munmap(q, 4096) == 0, "munmap of the mlock page returns 0");
}
}
check(mlockall(MCL_CURRENT | MCL_FUTURE) == 0, "mlockall(MCL_CURRENT|MCL_FUTURE) returns 0");
check(munlockall() == 0, "munlockall returns 0");
{
unsigned char *r;
r = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
check(r != MAP_FAILED && r != NULL, "mmap a page for posix_madvise");
if (r != MAP_FAILED && r != NULL)
{
check(posix_madvise(r, 4096, POSIX_MADV_DONTNEED) == 0,
"posix_madvise(POSIX_MADV_DONTNEED) returns 0");
check(munmap(r, 4096) == 0, "munmap of the posix_madvise page returns 0");
}
}
}
#if VLIBC_LEVEL_GE(2)
/* Level 2: madvise and POSIX shared memory (real /dev/shm objects). */
static void
level2_scenarios(void)
{
const char *name = "vlibc-t33-shm";
unsigned char *p;
char rbuf[16];
int fd;
int fd2;
p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
check(p != MAP_FAILED && p != NULL, "mmap a page for madvise");
if (p != MAP_FAILED && p != NULL)
{
p[0] = 'D';
check(madvise(p, 4096, MADV_DONTNEED) == 0, "madvise(MADV_DONTNEED) returns 0");
check(munmap(p, 4096) == 0, "munmap of the madvise page returns 0");
}
fd = shm_open(name, O_RDWR | O_CREAT, 0600);
check(fd >= 0, "shm_open creates the shared memory object");
if (fd < 0)
{
return;
}
check(write(fd, "shmdata", 7) == 7, "write to the shm object returns 7");
check(close(fd) == 0, "close of the shm object returns 0");
fd2 = shm_open(name, O_RDWR, 0);
check(fd2 >= 0, "shm_open re-opens the object without O_CREAT");
if (fd2 >= 0)
{
check(read(fd2, rbuf, sizeof(rbuf)) == 7, "read of the re-opened object returns 7");
check(rbuf[0] == 's' && rbuf[1] == 'h' && rbuf[2] == 'm' && rbuf[3] == 'd' &&
rbuf[4] == 'a' && rbuf[5] == 't' && rbuf[6] == 'a',
"the re-opened object holds the earlier write (file-backed)");
check(close(fd2) == 0, "close of the re-opened object returns 0");
}
check(shm_unlink(name) == 0, "shm_unlink removes the object");
}
#endif /* VLIBC_LEVEL_GE(2) */
/*
* Failure scenarios (-f): every assertion is on the return value only, and
* the process exits through raw SYS_exit_group because the library writes
* errno on these paths (host-TCB hazard).
*/
static int
failure_scenarios(void)
{
int rc = 0;
if (mmap((void *)0x1, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1,
0) != MAP_FAILED)
{
say(2, "FAIL: mmap MAP_FIXED at an unaligned address did not return MAP_FAILED\n");
rc = 1;
}
else
{
say(1, "PASS: mmap MAP_FIXED at 0x1 -> MAP_FAILED\n");
}
if (munmap((void *)0x1, 4096) != -1)
{
say(2, "FAIL: munmap of an unaligned address did not return -1\n");
rc = 1;
}
else
{
say(1, "PASS: munmap((void *)0x1, 4096) -> -1\n");
}
#if VLIBC_LEVEL_GE(2)
if (shm_open("bad/name", O_RDWR | O_CREAT, 0600) != -1)
{
say(2, "FAIL: shm_open of a name containing '/' did not return -1\n");
rc = 1;
}
else
{
say(1, "PASS: shm_open(\"bad/name\") -> -1\n");
}
#endif
return rc;
}
int
main(int argc, char **argv)
{
const char *path = "/tmp/vlibc-t33-shmfile";
unsigned long saved;
int rc;
if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'f')
{
/*
* The failure scenarios write errno inside the library; under the
* host libc that slot is glibc's private TLS state, so leave via
* the raw syscall without running host cleanup.
*/
rc = failure_scenarios();
__syscall1(SYS_exit_group, rc);
return rc; /* not reached */
}
/* Bracket the whole default-mode run against the host-TCB hazard. */
saved = tcb_slot1();
anon_scenarios();
file_and_lock_scenarios(path);
#if VLIBC_LEVEL_GE(2)
level2_scenarios();
#endif
__syscall3(SYS_unlinkat, AT_FDCWD, (long)path, 0);
if (failures > 0)
{
say(2, "FAILED: one or more mman checks failed\n");
rc = 1;
}
else
{
say(1, "all mman tests passed\n");
rc = 0;
}
tcb_slot1_set(saved);
return rc;
}