Data-Stack-Shared
view release on metacpan or search on metacpan
hdr->total_size = total;
hdr->data_off = sizeof(StkHeader);
hdr->ctl_off = stk_ctl_offset(elem_size, capacity);
__atomic_thread_fence(__ATOMIC_SEQ_CST);
/* Publish magic LAST, as a release store: it is the commit point, so a
creator killed before it leaves magic==0 and never a file mistaken for
a valid one. A kill during the field stores leaves one to remove by
hand. */
__atomic_store_n(&hdr->magic, STK_MAGIC, __ATOMIC_RELEASE);
}
/* Layout fields are passed in by the caller -- either from a validated
* header snapshot or locally computed -- never re-read from the live
* mapping, which a hostile peer could rewrite between validation and
* here (double-fetch TOCTOU). */
static inline StkHandle *stk_setup(void *base, size_t msize,
const char *path, int bfd,
uint64_t data_off, uint64_t ctl_off,
uint32_t elem_size, uint64_t capacity) {
StkHandle *h = (StkHandle *)calloc(1, sizeof(StkHandle));
if (!h) { munmap(base, msize); if (bfd >= 0) close(bfd); return NULL; }
h->hdr = (StkHeader *)base;
h->data = (uint8_t *)base + data_off;
h->ctl = (uint64_t *)((uint8_t *)base + ctl_off);
h->mmap_size = msize;
h->elem_size = elem_size; /* cached -- safe from shared-mem tampering */
h->capacity = capacity; /* cached -- trusted index/length bound */
h->path = path ? strdup(path) : NULL;
h->notify_fd = -1;
h->backing_fd = bfd;
return h;
}
/* Validate a mapped header (shared by stk_create reopen and stk_open_fd). */
static inline int stk_validate_header(const StkHeader *hdr, uint64_t file_size,
uint32_t expected_variant) {
if (hdr->magic != STK_MAGIC) return 0;
if (hdr->version != STK_VERSION) return 0;
if (hdr->variant_id != expected_variant) return 0;
if (hdr->elem_size == 0 || hdr->capacity == 0) return 0;
/* Pin elem_size to the variant so a crafted header can't drive an over-read/write
in push/pop: Int reads a fixed 8-byte slot; Str needs a 4-byte length prefix + data. */
if (expected_variant == STK_VAR_INT && hdr->elem_size != 8) return 0;
if (expected_variant == STK_VAR_STR && hdr->elem_size < 5) return 0;
if (hdr->capacity > 0x7FFFFFFFu) return 0;
if (hdr->total_size != file_size) return 0;
if (hdr->data_off != sizeof(StkHeader)) return 0;
if (hdr->ctl_off != stk_ctl_offset(hdr->elem_size, hdr->capacity)) return 0;
if (hdr->total_size != stk_total_size(hdr->elem_size, hdr->capacity)) return 0;
return 1;
}
/* Securely obtain a fd: create exclusively (O_CREAT|O_EXCL|O_NOFOLLOW at mode,
* default 0600), or attach an existing file (O_RDWR|O_NOFOLLOW, no O_CREAT).
* O_EXCL blocks a pre-seeded/hard-linked file and O_NOFOLLOW a symlink swap,
* so a local peer cannot redirect or pre-own the backing segment. */
static int stk_secure_open(const char *path, mode_t mode, char *errbuf) {
for (int attempt = 0; attempt < 100; attempt++) {
int fd = open(path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW|O_CLOEXEC, mode);
if (fd >= 0) { (void)fchmod(fd, mode); return fd; } /* exact mode: umask narrowed the O_EXCL create */
if (errno != EEXIST) { STK_ERR("create %s: %s", path, strerror(errno)); return -1; }
fd = open(path, O_RDWR|O_NOFOLLOW|O_CLOEXEC);
if (fd >= 0) return fd;
if (errno == ENOENT) continue; /* creator unlinked between our two opens; retry */
STK_ERR("open %s: %s", path, strerror(errno)); /* ELOOP => symlink rejected */
return -1;
}
STK_ERR("open %s: create/attach kept racing", path);
return -1;
}
/* True iff the whole mapped region is zero -- what an abandoned mid-init
creator leaves. Lets recovery re-init only a provably-empty file, never
one that merely starts with a zero word. Cold path, so a byte scan is
fine. */
static inline int stk_region_is_zero(const void *p, size_t n) {
const unsigned char *b = (const unsigned char *)p;
for (size_t i = 0; i < n; i++) if (b[i]) return 0;
return 1;
}
static StkHandle *stk_create(const char *path, uint64_t capacity,
uint32_t elem_size, uint32_t variant_id,
mode_t mode, char *errbuf) {
if (errbuf) errbuf[0] = '\0';
if (capacity == 0) { STK_ERR("capacity must be > 0"); return NULL; }
if (capacity > 0x7FFFFFFFu) { STK_ERR("capacity too large (max 2147483647)"); return NULL; }
if (elem_size == 0) { STK_ERR("elem_size must be > 0"); return NULL; }
if (capacity > (UINT64_MAX - sizeof(StkHeader) - 16) / (elem_size + sizeof(uint64_t))) {
STK_ERR("capacity * elem_size overflow"); return NULL;
}
uint64_t total = stk_total_size(elem_size, capacity);
int anonymous = (path == NULL);
int fd = -1;
size_t map_size;
void *base;
if (anonymous) {
map_size = (size_t)total;
base = mmap(NULL, map_size, PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_ANONYMOUS, -1, 0);
if (base == MAP_FAILED) { STK_ERR("mmap: %s", strerror(errno)); return NULL; }
} else {
fd = stk_secure_open(path, mode, errbuf);
if (fd < 0) return NULL;
if (flock(fd, LOCK_EX) < 0) { STK_ERR("flock: %s", strerror(errno)); close(fd); return NULL; }
struct stat st;
if (fstat(fd, &st) < 0) {
STK_ERR("fstat: %s", strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL;
}
int is_new = (st.st_size == 0);
if (!is_new && (uint64_t)st.st_size < sizeof(StkHeader)) {
STK_ERR("%s: file too small (%lld)", path, (long long)st.st_size);
flock(fd, LOCK_UN); close(fd); return NULL;
}
if (is_new && (st.st_uid != geteuid() || fchmod(fd, mode) < 0)) {
STK_ERR("%s: refusing to initialize file not owned by us", path);
flock(fd, LOCK_UN); close(fd); return NULL;
}
if (is_new) {
if (ftruncate(fd, (off_t)total) < 0) {
STK_ERR("ftruncate: %s", strerror(errno));
flock(fd, LOCK_UN); close(fd); return NULL;
}
}
map_size = is_new ? (size_t)total : (size_t)st.st_size;
base = mmap(NULL, map_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (base == MAP_FAILED) { STK_ERR("mmap: %s", strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL; }
if (!is_new) {
StkHeader snap; /* single fetch: validate + setup use one copy */
memcpy(&snap, base, sizeof snap);
if (!stk_validate_header(&snap, (uint64_t)st.st_size, variant_id)) {
/* Recover an abandoned mid-init file: a creator killed
* between the ftruncate and the header init leaves a
* full-size, all-zero (magic==0) file that would brick every
* future open of this path. Re-initialize ONLY when it is
* exactly our size, still uninitialized, and owned by us;
* anything else still errors. */
if (snap.magic == 0 && (uint64_t)st.st_size == total
&& st.st_uid == geteuid() && stk_region_is_zero(base, map_size)) {
if (fchmod(fd, mode) < 0) {
STK_ERR("%s: fchmod: %s", path, strerror(errno));
munmap(base, map_size); flock(fd, LOCK_UN); close(fd); return NULL;
}
stk_init_header(base, total, elem_size, variant_id, capacity);
flock(fd, LOCK_UN); close(fd);
return stk_setup(base, map_size, path, -1,
sizeof(StkHeader), stk_ctl_offset(elem_size, capacity),
elem_size, capacity);
}
if (snap.magic == 0 && (uint64_t)st.st_size == total
&& st.st_uid == geteuid())
STK_ERR("incomplete stack file left by an interrupted create; remove it and retry");
else
STK_ERR("invalid or incompatible stack file");
munmap(base, map_size); flock(fd, LOCK_UN); close(fd); return NULL;
}
flock(fd, LOCK_UN); close(fd);
return stk_setup(base, map_size, path, -1,
snap.data_off, snap.ctl_off,
snap.elem_size, snap.capacity);
}
}
stk_init_header(base, total, elem_size, variant_id, capacity);
if (fd >= 0) { flock(fd, LOCK_UN); close(fd); }
return stk_setup(base, map_size, path, -1,
sizeof(StkHeader), stk_ctl_offset(elem_size, capacity),
elem_size, capacity);
}
static StkHandle *stk_create_memfd(const char *name, uint64_t capacity,
uint32_t elem_size, uint32_t variant_id,
char *errbuf) {
if (errbuf) errbuf[0] = '\0';
if (capacity == 0) { STK_ERR("capacity must be > 0"); return NULL; }
if (capacity > 0x7FFFFFFFu) { STK_ERR("capacity too large (max 2147483647)"); return NULL; }
if (elem_size == 0) { STK_ERR("elem_size must be > 0"); return NULL; }
if (capacity > (UINT64_MAX - sizeof(StkHeader) - 16) / (elem_size + sizeof(uint64_t))) {
STK_ERR("capacity * elem_size overflow"); return NULL;
}
uint64_t total = stk_total_size(elem_size, capacity);
int fd = memfd_create(name ? name : "stack", MFD_CLOEXEC | MFD_ALLOW_SEALING);
if (fd < 0) { STK_ERR("memfd_create: %s", strerror(errno)); return NULL; }
if (ftruncate(fd, (off_t)total) < 0) { STK_ERR("ftruncate: %s", strerror(errno)); close(fd); return NULL; }
(void)fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW);
void *base = mmap(NULL, (size_t)total, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (base == MAP_FAILED) { STK_ERR("mmap: %s", strerror(errno)); close(fd); return NULL; }
stk_init_header(base, total, elem_size, variant_id, capacity);
return stk_setup(base, (size_t)total, NULL, fd,
sizeof(StkHeader), stk_ctl_offset(elem_size, capacity),
elem_size, capacity);
}
static StkHandle *stk_open_fd(int fd, uint32_t variant_id, char *errbuf) {
if (errbuf) errbuf[0] = '\0';
struct stat st;
if (fstat(fd, &st) < 0) { STK_ERR("fstat: %s", strerror(errno)); return NULL; }
if ((uint64_t)st.st_size < sizeof(StkHeader)) { STK_ERR("too small"); return NULL; }
size_t ms = (size_t)st.st_size;
void *base = mmap(NULL, ms, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
( run in 0.988 second using v1.01-cache-2.11-cpan-f03e8824b8d )