Data-Stack-Shared

 view release on metacpan or  search on metacpan

stack.h  view on Meta::CPAN

    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 )