File Coverage

Shared.xs
Criterion Covered Total %
statement 94 95 98.9
branch 90 166 54.2
condition n/a
subroutine n/a
pod n/a
total 184 261 70.5


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5             #include "ppport.h"
6             #include "intern.h"
7              
8             #define EXTRACT(sv) \
9             if (!sv_isobject(sv) || !sv_derived_from(sv, "Data::Intern::Shared")) \
10             croak("Expected a Data::Intern::Shared object"); \
11             SiHandle *h = INT2PTR(SiHandle*, SvIV(SvRV(sv))); \
12             if (!h) croak("Attempted to use a destroyed Data::Intern::Shared object")
13              
14             #define MAKE_OBJ(class, handle) \
15             SV *obj = newSViv(PTR2IV(handle)); \
16             SV *ref = newRV_noinc(obj); \
17             sv_bless(ref, gv_stashpv(class, GV_ADD)); \
18             RETVAL = ref
19              
20             MODULE = Data::Intern::Shared PACKAGE = Data::Intern::Shared
21              
22             PROTOTYPES: DISABLE
23              
24             SV *
25             new(class, path, max_strings, arena_bytes = 0, ...)
26             const char *class
27             SV *path
28             UV max_strings
29             UV arena_bytes
30             PREINIT:
31             char errbuf[SI_ERR_BUFLEN];
32             CODE:
33 18 50         const char *p = (SvGETMAGIC(path), SvOK(path)) ? SvPV_nolen(path) : NULL;
    0          
    100          
34             /* Opt-in file mode for cross-user sharing; default 0600 (owner-only). */
35 18 50         mode_t mode = (items > 4 && (SvGETMAGIC(ST(4)), SvOK(ST(4)))) ? (mode_t)SvUV(ST(4)) : 0600;
    0          
    0          
    0          
36 18 50         if (max_strings > SI_MAX_STRINGS) croak("Data::Intern::Shared->new: max_strings exceeds 2^30");
37 18 50         if (arena_bytes > UINT32_MAX) croak("Data::Intern::Shared->new: arena_bytes exceeds 2^32");
38 18           SiHandle *h = si_create(p, (uint32_t)max_strings, (uint32_t)arena_bytes, mode, errbuf);
39 18 100         if (!h) croak("Data::Intern::Shared->new: %s", errbuf);
40 16           MAKE_OBJ(class, h);
41             OUTPUT:
42             RETVAL
43              
44             SV *
45             new_memfd(class, name, max_strings, arena_bytes = 0)
46             const char *class
47             SV *name
48             UV max_strings
49             UV arena_bytes
50             PREINIT:
51             char errbuf[SI_ERR_BUFLEN];
52             CODE:
53 2 50         const char *nm = (SvGETMAGIC(name), SvOK(name)) ? SvPV_nolen(name) : NULL; /* undef -> default label */
    0          
    100          
54 2 50         if (max_strings > SI_MAX_STRINGS) croak("Data::Intern::Shared->new_memfd: max_strings exceeds 2^30");
55 2 50         if (arena_bytes > UINT32_MAX) croak("Data::Intern::Shared->new_memfd: arena_bytes exceeds 2^32");
56 2           SiHandle *h = si_create_memfd(nm, (uint32_t)max_strings, (uint32_t)arena_bytes, errbuf);
57 2 50         if (!h) croak("Data::Intern::Shared->new_memfd: %s", errbuf);
58 2           MAKE_OBJ(class, h);
59             OUTPUT:
60             RETVAL
61              
62             SV *
63             new_from_fd(class, fd)
64             const char *class
65             int fd
66             PREINIT:
67             char errbuf[SI_ERR_BUFLEN];
68             CODE:
69 1           SiHandle *h = si_open_fd(fd, errbuf);
70 1 50         if (!h) croak("Data::Intern::Shared->new_from_fd: %s", errbuf);
71 1           MAKE_OBJ(class, h);
72             OUTPUT:
73             RETVAL
74              
75             void
76             DESTROY(self)
77             SV *self
78             CODE:
79 20 50         if (sv_isobject(self) && sv_derived_from(self, "Data::Intern::Shared")) {
    50          
80 20           SiHandle *h = INT2PTR(SiHandle*, SvIV(SvRV(self)));
81 20 100         if (h) { sv_setiv(SvRV(self), 0); si_destroy(h); } /* null first: activates EXTRACT's use-after-destroy croak + makes a double DESTROY a no-op */
82             }
83              
84             UV
85             count(self)
86             SV *self
87             PREINIT:
88 16 50         EXTRACT(self);
    50          
    100          
89             CODE:
90 15           si_rwlock_rdlock(h);
91 15           RETVAL = h->hdr->count;
92 15           si_rwlock_rdunlock(h);
93             OUTPUT:
94             RETVAL
95              
96             UV
97             max_strings(self)
98             SV *self
99             PREINIT:
100 3 50         EXTRACT(self);
    50          
    50          
101             CODE:
102 3 50         RETVAL = h->hdr->max_strings;
103             OUTPUT:
104             RETVAL
105              
106             UV
107             arena_bytes(self)
108             SV *self
109             PREINIT:
110 3 50         EXTRACT(self);
    50          
    50          
111             CODE:
112 3 50         RETVAL = h->hdr->arena_bytes;
113             OUTPUT:
114             RETVAL
115              
116             UV
117             arena_used(self)
118             SV *self
119             PREINIT:
120 3 50         EXTRACT(self);
    50          
    50          
121             CODE:
122 3           si_rwlock_rdlock(h);
123 3           RETVAL = h->hdr->arena_used;
124 3           si_rwlock_rdunlock(h);
125             OUTPUT:
126             RETVAL
127              
128             void
129             clear(self)
130             SV *self
131             PREINIT:
132 2 50         EXTRACT(self);
    50          
    50          
133             CODE:
134 2           si_rwlock_wrlock(h);
135 2           si_clear_locked(h);
136 2           si_rwlock_wrunlock(h);
137              
138             SV *
139             intern(self, str)
140             SV *self
141             SV *str
142             PREINIT:
143 41539 50         EXTRACT(self);
    50          
    50          
144             STRLEN n;
145             const char *s;
146             int64_t id;
147             CODE:
148 41539           s = SvPVbyte(str, n);
149 41539           si_rwlock_wrlock(h);
150 41539           id = si_intern_locked(h, s, n);
151 41539           __atomic_fetch_add(&h->hdr->stat_ops, 1, __ATOMIC_RELAXED);
152 41539           si_rwlock_wrunlock(h);
153 41539 100         RETVAL = (id < 0) ? &PL_sv_undef : newSVuv((UV)id);
154             OUTPUT:
155             RETVAL
156              
157             SV *
158             id_of(self, str)
159             SV *self
160             SV *str
161             PREINIT:
162 37013 50         EXTRACT(self);
    50          
    50          
163             STRLEN n;
164             const char *s;
165             uint32_t id;
166             CODE:
167 37013           s = SvPVbyte(str, n);
168 37013           si_rwlock_rdlock(h);
169 37013           int found = si_id_of_locked(h, s, n, &id);
170 37013           si_rwlock_rdunlock(h);
171 37013 100         RETVAL = found ? newSVuv(id) : &PL_sv_undef;
172             OUTPUT:
173             RETVAL
174              
175             SV *
176             string(self, id)
177             SV *self
178             UV id
179             PREINIT:
180 37017 50         EXTRACT(self);
    50          
    50          
181             CODE:
182 37017           si_rwlock_rdlock(h);
183             {
184 37017           char *tmp = NULL; uint32_t tl = 0; int found = 0;
185             /* id, reverse[id] and the arena length prefix are all file-stored and
186             * attacker-controlled; bound each before dereferencing so a corrupted
187             * segment yields undef instead of an out-of-bounds read/trap. Valid
188             * data always satisfies off+4+len <= arena_used, so behavior is
189             * unchanged in normal use. */
190 37017 100         if (id < h->hdr->count) {
191 37015           uint32_t off = h->reverse[id];
192 37015           uint32_t used = h->hdr->arena_used;
193 37015 50         if (off <= used && used - off >= sizeof(uint32_t)) {
    50          
194             uint32_t l;
195 37015           const char *str = si_arena_str(h, off, &l);
196 37015 50         if (l <= used - off - (uint32_t)sizeof(uint32_t)) {
197 37015           found = 1; tl = l;
198             /* Copy out with a non-croaking malloc so the SV can be
199             * built AFTER unlocking: newSVpvn can croak on OOM, and a
200             * croak while holding the rdlock would leak it and wedge
201             * every writer. */
202 37015 100         if (l) { tmp = (char *)malloc(l); if (tmp) memcpy(tmp, str, l); }
    50          
203             }
204             }
205             }
206 37017           si_rwlock_rdunlock(h);
207 37017 100         if (!found) RETVAL = &PL_sv_undef;
208 37015 100         else if (tl == 0) RETVAL = newSVpvn("", 0);
209 37013 50         else if (tmp) { RETVAL = newSVpvn(tmp, tl); free(tmp); }
210 0           else croak("Data::Intern::Shared->string: out of memory");
211             }
212             OUTPUT:
213             RETVAL
214              
215             bool
216             exists(self, str)
217             SV *self
218             SV *str
219             PREINIT:
220 35611 50         EXTRACT(self);
    50          
    50          
221             STRLEN n;
222             const char *s;
223             uint32_t id;
224             CODE:
225 35611           s = SvPVbyte(str, n);
226 35611           si_rwlock_rdlock(h);
227 35611           RETVAL = si_id_of_locked(h, s, n, &id);
228 35611           si_rwlock_rdunlock(h);
229             OUTPUT:
230             RETVAL
231              
232             SV *
233             stats(self)
234             SV *self
235             PREINIT:
236 1 50         EXTRACT(self);
    50          
    50          
237             CODE:
238             {
239 1           HV *hv = newHV();
240 1           si_rwlock_rdlock(h);
241 1           SiHeader *hd = h->hdr;
242 1           hv_stores(hv, "count", newSVuv(hd->count));
243 1           hv_stores(hv, "max_strings", newSVuv(hd->max_strings));
244 1           hv_stores(hv, "hash_slots", newSVuv(hd->hash_slots));
245 1           hv_stores(hv, "hash_load", newSVnv((double)hd->count / (double)hd->hash_slots));
246 1           hv_stores(hv, "arena_used", newSVuv(hd->arena_used));
247 1           hv_stores(hv, "arena_bytes", newSVuv(hd->arena_bytes));
248 1           hv_stores(hv, "arena_load", newSVnv((double)hd->arena_used / (double)hd->arena_bytes));
249 1           hv_stores(hv, "ops", newSVuv(hd->stat_ops));
250 1           hv_stores(hv, "mmap_size", newSVuv((UV)h->mmap_size));
251 1           si_rwlock_rdunlock(h);
252 1           RETVAL = newRV_noinc((SV *)hv);
253             }
254             OUTPUT:
255             RETVAL
256              
257             SV *
258             path(self)
259             SV *self
260             PREINIT:
261 4 50         EXTRACT(self);
    50          
    50          
262             CODE:
263 4 100         RETVAL = h->path ? newSVpv(h->path, 0) : &PL_sv_undef;
264             OUTPUT:
265             RETVAL
266              
267             int
268             memfd(self)
269             SV *self
270             PREINIT:
271 4 50         EXTRACT(self);
    50          
    50          
272             CODE:
273 4 50         RETVAL = h->backing_fd;
274             OUTPUT:
275             RETVAL
276              
277             void
278             sync(self)
279             SV *self
280             PREINIT:
281 4 50         EXTRACT(self);
    50          
    50          
282             CODE:
283 4 50         if (si_msync(h) != 0) croak("sync: %s", strerror(errno));
284              
285             void
286             unlink(self, ...)
287             SV *self
288             CODE:
289 3 100         if (sv_isobject(self) && sv_derived_from(self, "Data::Intern::Shared")) {
    50          
290 1           SiHandle *h = INT2PTR(SiHandle*, SvIV(SvRV(self)));
291 1 50         if (h && h->path) unlink(h->path);
    50          
292 1 50         } else if (items >= 2 && (SvGETMAGIC(ST(1)), SvOK(ST(1)))) {
    50          
    0          
    50          
293 1           unlink(SvPV_nolen(ST(1)));
294             }