File Coverage

Binmode.xs
Criterion Covered Total %
statement 119 122 97.5
branch 104 172 60.4
condition n/a
subroutine n/a
pod n/a
total 223 294 75.8


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              
6             #include
7              
8             #include "ppport.h"
9              
10             #define DEBUG 0
11              
12             /* A duplicate of PL_ppaddr as we find it at BOOT time.
13             We can thus overwrite PL_ppaddr with our own wrapper functions.
14             This interacts better with wrap_op_checker(), which doesn’t provide
15             a good way to call the op’s (now-overwritten) op_ppaddr callback.
16             */
17             static Perl_ppaddr_t ORIG_PL_ppaddr[OP_max];
18              
19             #define MYPKG "Sys::Binmode"
20             #define HINT_KEY MYPKG "/enabled"
21              
22             /* An idempotent variant of dMARK that allows us to inspect the
23             mark stack without changing it: */
24             #ifndef dMARK_TOPMARK
25             #define dMARK_TOPMARK SV **mark = PL_stack_base + TOPMARK
26             #endif
27              
28             #define DOWNGRADE_SVPV(sv) if (SvPOK(sv) && SvUTF8(sv)) sv_utf8_downgrade(sv, FALSE)
29              
30 100           PERL_STATIC_INLINE void MY_DOWNGRADE(pTHX_ SV** svp) {
31 101 50         if (UNLIKELY(SvGAMAGIC(*svp))) {
    100          
    50          
    100          
    50          
    100          
32              
33             /* If the parameter in question is magical/overloaded
34             then we need to fetch the (string) value, downgrade it,
35             then replace the overloaded object in the stack with
36             our fetched value.
37             */
38              
39 1           SV* replacement = sv_newmortal();
40              
41             /* fetches the overloadeed value */
42 1           sv_copypv(replacement, *svp);
43              
44 1 50         DOWNGRADE_SVPV(replacement);
    50          
45              
46 1           *svp = replacement;
47             }
48              
49             /* NB: READONLY strings can be downgraded. */
50 99 100         else DOWNGRADE_SVPV(*svp);
    100          
51 100           }
52              
53             #define BINMODE_IS_ON (cop_hints_fetch_pvs(PL_curcop, HINT_KEY, 0) != &PL_sv_placeholder)
54              
55             /* For ops that take an indefinite number of args. */
56             #define MAKE_OPEN_LIST_WRAPPER(OPID) MAKE_CAPPED_LIST_WRAPPER(OPID, 0)
57              
58             /* For ops whose number of string args is a fixed range.
59              
60             NB: In some perls, some list opts don’t set MARK. In those cases we
61             fall back to MAXARG. As of now mkdir is the known “offender”, and
62             only on Alpine Linux 3.11 & 3.12 (not 3.13).
63             */
64             #define MAKE_CAPPED_LIST_WRAPPER(OPID, OP_MAXARG) \
65             static OP* _wrapped_pp_##OPID(pTHX) { \
66             if (BINMODE_IS_ON) { \
67             dSP; \
68             dMARK_TOPMARK; \
69             \
70             /* The compiler should optimize this away \
71             for MAKE_OPEN_LIST_WRAPPER: \
72             */ \
73             if (OP_MAXARG) \
74             if (SP < MARK || (SP - MARK) > OP_MAXARG) { \
75             unsigned numargs = MAXARG; \
76             MARK = SP; \
77             while (numargs--) MARK--; \
78             } \
79             \
80             while (++MARK <= SP) MY_DOWNGRADE(aTHX_ MARK); \
81             } \
82             \
83             return ORIG_PL_ppaddr[OPID](aTHX); \
84             }
85              
86             /* For ops that take a fixed number of args. */
87             #define MAKE_FIXED_LIST_WRAPPER(OPID, NUMARGS) \
88             static OP* _wrapped_pp_##OPID(pTHX) { \
89             if (BINMODE_IS_ON) { \
90             unsigned numargs = NUMARGS; \
91             dSP; \
92             while (numargs--) MY_DOWNGRADE(aTHX_ SP--); \
93             } \
94             \
95             return ORIG_PL_ppaddr[OPID](aTHX); \
96             }
97              
98             /* For ops where only the last arg is a string. */
99             #define MAKE_SP_WRAPPER(OPID) \
100             static OP* _wrapped_pp_##OPID(pTHX) { \
101             if (BINMODE_IS_ON) { \
102             dSP; \
103             MY_DOWNGRADE(aTHX_ SP); \
104             } \
105             \
106             return ORIG_PL_ppaddr[OPID](aTHX); \
107             }
108              
109              
110 42 100         MAKE_OPEN_LIST_WRAPPER(OP_OPEN);
    100          
111 5 50         MAKE_CAPPED_LIST_WRAPPER(OP_SYSOPEN, 4);
    50          
    50          
    0          
    100          
112 4 50         MAKE_FIXED_LIST_WRAPPER(OP_TRUNCATE, 2);
    100          
113 4 50         MAKE_OPEN_LIST_WRAPPER(OP_EXEC);
    100          
114 8 50         MAKE_OPEN_LIST_WRAPPER(OP_SYSTEM);
    100          
115              
116 6 100         MAKE_SP_WRAPPER(OP_BIND);
117 2 50         MAKE_SP_WRAPPER(OP_CONNECT);
118 4 50         MAKE_SP_WRAPPER(OP_SSOCKOPT);
119 2 50         MAKE_SP_WRAPPER(OP_SEND);
120              
121 0 0         MAKE_SP_WRAPPER(OP_IOCTL);
122              
123 2 50         MAKE_SP_WRAPPER(OP_LSTAT);
124 2 50         MAKE_SP_WRAPPER(OP_STAT);
125 2 50         MAKE_SP_WRAPPER(OP_FTRREAD);
126 2 50         MAKE_SP_WRAPPER(OP_FTRWRITE);
127 2 50         MAKE_SP_WRAPPER(OP_FTREXEC);
128 2 50         MAKE_SP_WRAPPER(OP_FTEREAD);
129 2 50         MAKE_SP_WRAPPER(OP_FTEWRITE);
130 2 50         MAKE_SP_WRAPPER(OP_FTEEXEC);
131 6 100         MAKE_SP_WRAPPER(OP_FTIS);
132 2 50         MAKE_SP_WRAPPER(OP_FTSIZE);
133 2 50         MAKE_SP_WRAPPER(OP_FTMTIME);
134 2 50         MAKE_SP_WRAPPER(OP_FTATIME);
135 2 50         MAKE_SP_WRAPPER(OP_FTCTIME);
136 2 50         MAKE_SP_WRAPPER(OP_FTROWNED);
137 2 50         MAKE_SP_WRAPPER(OP_FTEOWNED);
138 2 50         MAKE_SP_WRAPPER(OP_FTZERO);
139 2 50         MAKE_SP_WRAPPER(OP_FTSOCK);
140 2 50         MAKE_SP_WRAPPER(OP_FTCHR);
141 2 50         MAKE_SP_WRAPPER(OP_FTBLK);
142 2 50         MAKE_SP_WRAPPER(OP_FTFILE);
143 2 50         MAKE_SP_WRAPPER(OP_FTDIR);
144 2 50         MAKE_SP_WRAPPER(OP_FTPIPE);
145 2 50         MAKE_SP_WRAPPER(OP_FTSUID);
146 2 50         MAKE_SP_WRAPPER(OP_FTSGID);
147 2 50         MAKE_SP_WRAPPER(OP_FTSVTX);
148 2 50         MAKE_SP_WRAPPER(OP_FTLINK);
149             /* MAKE_SP_WRAPPER(OP_FTTTY); */
150 2 50         MAKE_SP_WRAPPER(OP_FTTEXT);
151 2 50         MAKE_SP_WRAPPER(OP_FTBINARY);
152 8 100         MAKE_SP_WRAPPER(OP_CHDIR);
153 5 50         MAKE_OPEN_LIST_WRAPPER(OP_CHOWN);
    100          
154 0 0         MAKE_SP_WRAPPER(OP_CHROOT);
155 3 50         MAKE_OPEN_LIST_WRAPPER(OP_UNLINK);
    100          
156 6 100         MAKE_OPEN_LIST_WRAPPER(OP_CHMOD);
    100          
157 5 50         MAKE_OPEN_LIST_WRAPPER(OP_UTIME);
    100          
158 4 50         MAKE_FIXED_LIST_WRAPPER(OP_RENAME, 2);
    100          
159 4 50         MAKE_FIXED_LIST_WRAPPER(OP_LINK, 2);
    100          
160 4 50         MAKE_FIXED_LIST_WRAPPER(OP_SYMLINK, 2);
    100          
161 4 50         MAKE_SP_WRAPPER(OP_READLINK);
162 9 50         MAKE_CAPPED_LIST_WRAPPER(OP_MKDIR, 2);
    50          
    50          
    0          
    100          
163 4 50         MAKE_SP_WRAPPER(OP_RMDIR);
164 2 50         MAKE_SP_WRAPPER(OP_OPEN_DIR);
165              
166 1289 100         MAKE_SP_WRAPPER(OP_REQUIRE);
167 2 50         MAKE_SP_WRAPPER(OP_DOFILE);
168 2 50         MAKE_SP_WRAPPER(OP_BACKTICK);
169              
170             /* (These appear to be fine already.)
171             MAKE_SP_WRAPPER(OP_GHBYADDR);
172             MAKE_SP_WRAPPER(OP_GNBYADDR);
173             */
174              
175 0 0         MAKE_OPEN_LIST_WRAPPER(OP_SYSCALL);
    0          
176              
177             /* ---------------------------------------------------------------------- */
178              
179             #define MAKE_BOOT_WRAPPER(OPID) \
180             ORIG_PL_ppaddr[OPID] = PL_ppaddr[OPID]; \
181             PL_ppaddr[OPID] = _wrapped_pp_##OPID;
182              
183             //----------------------------------------------------------------------
184              
185             bool initialized = false;
186              
187             MODULE = Sys::Binmode PACKAGE = Sys::Binmode
188              
189             PROTOTYPES: DISABLE
190              
191             BOOT:
192             /* In theory this is for PL_check rather than PL_ppaddr, but per
193             Paul Evans in practice this mutex gets used for other stuff, too.
194             Paul says a race here should be exceptionally rare, so for pre-5.16
195             perls (which lack this mutex) let’s just skip it.
196             */
197             #ifdef OP_CHECK_MUTEX_LOCK
198             OP_CHECK_MUTEX_LOCK;
199             #endif
200 13 50         if (!initialized) {
201 13           initialized = true;
202              
203 13           HV *stash = gv_stashpv(MYPKG, FALSE);
204 13           newCONSTSUB(stash, "_HINT_KEY", newSVpvs(HINT_KEY));
205              
206 13           MAKE_BOOT_WRAPPER(OP_OPEN);
207 13           MAKE_BOOT_WRAPPER(OP_SYSOPEN);
208 13           MAKE_BOOT_WRAPPER(OP_TRUNCATE);
209 13           MAKE_BOOT_WRAPPER(OP_EXEC);
210 13           MAKE_BOOT_WRAPPER(OP_SYSTEM);
211              
212 13           MAKE_BOOT_WRAPPER(OP_BIND);
213 13           MAKE_BOOT_WRAPPER(OP_CONNECT);
214 13           MAKE_BOOT_WRAPPER(OP_SSOCKOPT);
215 13           MAKE_BOOT_WRAPPER(OP_SEND);
216              
217 13           MAKE_BOOT_WRAPPER(OP_IOCTL);
218              
219 13           MAKE_BOOT_WRAPPER(OP_LSTAT);
220 13           MAKE_BOOT_WRAPPER(OP_STAT);
221 13           MAKE_BOOT_WRAPPER(OP_FTRREAD);
222 13           MAKE_BOOT_WRAPPER(OP_FTRWRITE);
223 13           MAKE_BOOT_WRAPPER(OP_FTREXEC);
224 13           MAKE_BOOT_WRAPPER(OP_FTEREAD);
225 13           MAKE_BOOT_WRAPPER(OP_FTEWRITE);
226 13           MAKE_BOOT_WRAPPER(OP_FTEEXEC);
227 13           MAKE_BOOT_WRAPPER(OP_FTIS);
228 13           MAKE_BOOT_WRAPPER(OP_FTSIZE);
229 13           MAKE_BOOT_WRAPPER(OP_FTMTIME);
230 13           MAKE_BOOT_WRAPPER(OP_FTATIME);
231 13           MAKE_BOOT_WRAPPER(OP_FTCTIME);
232 13           MAKE_BOOT_WRAPPER(OP_FTROWNED);
233 13           MAKE_BOOT_WRAPPER(OP_FTEOWNED);
234 13           MAKE_BOOT_WRAPPER(OP_FTZERO);
235 13           MAKE_BOOT_WRAPPER(OP_FTSOCK);
236 13           MAKE_BOOT_WRAPPER(OP_FTCHR);
237 13           MAKE_BOOT_WRAPPER(OP_FTBLK);
238 13           MAKE_BOOT_WRAPPER(OP_FTFILE);
239 13           MAKE_BOOT_WRAPPER(OP_FTDIR);
240 13           MAKE_BOOT_WRAPPER(OP_FTPIPE);
241 13           MAKE_BOOT_WRAPPER(OP_FTSUID);
242 13           MAKE_BOOT_WRAPPER(OP_FTSGID);
243 13           MAKE_BOOT_WRAPPER(OP_FTSVTX);
244 13           MAKE_BOOT_WRAPPER(OP_FTLINK);
245             /* MAKE_BOOT_WRAPPER(OP_FTTTY); */
246 13           MAKE_BOOT_WRAPPER(OP_FTTEXT);
247 13           MAKE_BOOT_WRAPPER(OP_FTBINARY);
248 13           MAKE_BOOT_WRAPPER(OP_CHDIR);
249 13           MAKE_BOOT_WRAPPER(OP_CHOWN);
250 13           MAKE_BOOT_WRAPPER(OP_CHROOT);
251 13           MAKE_BOOT_WRAPPER(OP_UNLINK);
252 13           MAKE_BOOT_WRAPPER(OP_CHMOD);
253 13           MAKE_BOOT_WRAPPER(OP_UTIME);
254 13           MAKE_BOOT_WRAPPER(OP_RENAME);
255 13           MAKE_BOOT_WRAPPER(OP_LINK);
256 13           MAKE_BOOT_WRAPPER(OP_SYMLINK);
257 13           MAKE_BOOT_WRAPPER(OP_READLINK);
258 13           MAKE_BOOT_WRAPPER(OP_MKDIR);
259 13           MAKE_BOOT_WRAPPER(OP_RMDIR);
260 13           MAKE_BOOT_WRAPPER(OP_OPEN_DIR);
261              
262 13           MAKE_BOOT_WRAPPER(OP_REQUIRE);
263 13           MAKE_BOOT_WRAPPER(OP_DOFILE);
264 13           MAKE_BOOT_WRAPPER(OP_BACKTICK);
265              
266             /* (These appear to be fine already.)
267             MAKE_BOOT_WRAPPER(OP_GHBYADDR);
268             MAKE_BOOT_WRAPPER(OP_GNBYADDR);
269             */
270              
271 13           MAKE_BOOT_WRAPPER(OP_SYSCALL);
272             }
273             #ifdef OP_CHECK_MUTEX_UNLOCK
274             OP_CHECK_MUTEX_UNLOCK;
275             #endif