15
15
#include <sys/types.h>
16
16
#include <sys/wait.h>
17
17
18
+ #include "../../pidfd/pidfd.h"
18
19
#include "../../kselftest_harness.h"
19
20
20
21
#define clean_errno () (errno == 0 ? "None" : strerror(errno))
26
27
#define SCM_PIDFD 0x04
27
28
#endif
28
29
30
+ #define CHILD_EXIT_CODE_OK 123
31
+
29
32
static void child_die ()
30
33
{
31
34
exit (1 );
@@ -126,16 +129,65 @@ static pid_t get_pid_from_fdinfo_file(int pidfd, const char *key, size_t keylen)
126
129
return result ;
127
130
}
128
131
132
+ struct cmsg_data {
133
+ struct ucred * ucred ;
134
+ int * pidfd ;
135
+ };
136
+
137
+ static int parse_cmsg (struct msghdr * msg , struct cmsg_data * res )
138
+ {
139
+ struct cmsghdr * cmsg ;
140
+ int data = 0 ;
141
+
142
+ if (msg -> msg_flags & (MSG_TRUNC | MSG_CTRUNC )) {
143
+ log_err ("recvmsg: truncated" );
144
+ return 1 ;
145
+ }
146
+
147
+ for (cmsg = CMSG_FIRSTHDR (msg ); cmsg != NULL ;
148
+ cmsg = CMSG_NXTHDR (msg , cmsg )) {
149
+ if (cmsg -> cmsg_level == SOL_SOCKET &&
150
+ cmsg -> cmsg_type == SCM_PIDFD ) {
151
+ if (cmsg -> cmsg_len < sizeof (* res -> pidfd )) {
152
+ log_err ("CMSG parse: SCM_PIDFD wrong len" );
153
+ return 1 ;
154
+ }
155
+
156
+ res -> pidfd = (void * )CMSG_DATA (cmsg );
157
+ }
158
+
159
+ if (cmsg -> cmsg_level == SOL_SOCKET &&
160
+ cmsg -> cmsg_type == SCM_CREDENTIALS ) {
161
+ if (cmsg -> cmsg_len < sizeof (* res -> ucred )) {
162
+ log_err ("CMSG parse: SCM_CREDENTIALS wrong len" );
163
+ return 1 ;
164
+ }
165
+
166
+ res -> ucred = (void * )CMSG_DATA (cmsg );
167
+ }
168
+ }
169
+
170
+ if (!res -> pidfd ) {
171
+ log_err ("CMSG parse: SCM_PIDFD not found" );
172
+ return 1 ;
173
+ }
174
+
175
+ if (!res -> ucred ) {
176
+ log_err ("CMSG parse: SCM_CREDENTIALS not found" );
177
+ return 1 ;
178
+ }
179
+
180
+ return 0 ;
181
+ }
182
+
129
183
static int cmsg_check (int fd )
130
184
{
131
185
struct msghdr msg = { 0 };
132
- struct cmsghdr * cmsg ;
186
+ struct cmsg_data res ;
133
187
struct iovec iov ;
134
- struct ucred * ucred = NULL ;
135
188
int data = 0 ;
136
189
char control [CMSG_SPACE (sizeof (struct ucred )) +
137
190
CMSG_SPACE (sizeof (int ))] = { 0 };
138
- int * pidfd = NULL ;
139
191
pid_t parent_pid ;
140
192
int err ;
141
193
@@ -158,53 +210,99 @@ static int cmsg_check(int fd)
158
210
return 1 ;
159
211
}
160
212
161
- for (cmsg = CMSG_FIRSTHDR (& msg ); cmsg != NULL ;
162
- cmsg = CMSG_NXTHDR (& msg , cmsg )) {
163
- if (cmsg -> cmsg_level == SOL_SOCKET &&
164
- cmsg -> cmsg_type == SCM_PIDFD ) {
165
- if (cmsg -> cmsg_len < sizeof (* pidfd )) {
166
- log_err ("CMSG parse: SCM_PIDFD wrong len" );
167
- return 1 ;
168
- }
213
+ /* send(pfd, "x", sizeof(char), 0) */
214
+ if (data != 'x' ) {
215
+ log_err ("recvmsg: data corruption" );
216
+ return 1 ;
217
+ }
169
218
170
- pidfd = (void * )CMSG_DATA (cmsg );
171
- }
219
+ if (parse_cmsg (& msg , & res )) {
220
+ log_err ("CMSG parse: parse_cmsg() failed" );
221
+ return 1 ;
222
+ }
172
223
173
- if (cmsg -> cmsg_level == SOL_SOCKET &&
174
- cmsg -> cmsg_type == SCM_CREDENTIALS ) {
175
- if (cmsg -> cmsg_len < sizeof (* ucred )) {
176
- log_err ("CMSG parse: SCM_CREDENTIALS wrong len" );
177
- return 1 ;
178
- }
224
+ /* pidfd from SCM_PIDFD should point to the parent process PID */
225
+ parent_pid =
226
+ get_pid_from_fdinfo_file (* res .pidfd , "Pid:" , sizeof ("Pid:" ) - 1 );
227
+ if (parent_pid != getppid ()) {
228
+ log_err ("wrong SCM_PIDFD %d != %d" , parent_pid , getppid ());
229
+ close (* res .pidfd );
230
+ return 1 ;
231
+ }
179
232
180
- ucred = (void * )CMSG_DATA (cmsg );
181
- }
233
+ close (* res .pidfd );
234
+ return 0 ;
235
+ }
236
+
237
+ static int cmsg_check_dead (int fd , int expected_pid )
238
+ {
239
+ int err ;
240
+ struct msghdr msg = { 0 };
241
+ struct cmsg_data res ;
242
+ struct iovec iov ;
243
+ int data = 0 ;
244
+ char control [CMSG_SPACE (sizeof (struct ucred )) +
245
+ CMSG_SPACE (sizeof (int ))] = { 0 };
246
+ pid_t client_pid ;
247
+ struct pidfd_info info = {
248
+ .mask = PIDFD_INFO_EXIT ,
249
+ };
250
+
251
+ iov .iov_base = & data ;
252
+ iov .iov_len = sizeof (data );
253
+
254
+ msg .msg_iov = & iov ;
255
+ msg .msg_iovlen = 1 ;
256
+ msg .msg_control = control ;
257
+ msg .msg_controllen = sizeof (control );
258
+
259
+ err = recvmsg (fd , & msg , 0 );
260
+ if (err < 0 ) {
261
+ log_err ("recvmsg" );
262
+ return 1 ;
182
263
}
183
264
184
- /* send(pfd, "x", sizeof(char), 0) */
185
- if (data != 'x' ) {
265
+ if (msg .msg_flags & (MSG_TRUNC | MSG_CTRUNC )) {
266
+ log_err ("recvmsg: truncated" );
267
+ return 1 ;
268
+ }
269
+
270
+ /* send(cfd, "y", sizeof(char), 0) */
271
+ if (data != 'y' ) {
186
272
log_err ("recvmsg: data corruption" );
187
273
return 1 ;
188
274
}
189
275
190
- if (! pidfd ) {
191
- log_err ("CMSG parse: SCM_PIDFD not found " );
276
+ if (parse_cmsg ( & msg , & res ) ) {
277
+ log_err ("CMSG parse: parse_cmsg() failed " );
192
278
return 1 ;
193
279
}
194
280
195
- if (!ucred ) {
196
- log_err ("CMSG parse: SCM_CREDENTIALS not found" );
281
+ /*
282
+ * pidfd from SCM_PIDFD should point to the client_pid.
283
+ * Let's read exit information and check if it's what
284
+ * we expect to see.
285
+ */
286
+ if (ioctl (* res .pidfd , PIDFD_GET_INFO , & info )) {
287
+ log_err ("%s: ioctl(PIDFD_GET_INFO) failed" , __func__ );
288
+ close (* res .pidfd );
197
289
return 1 ;
198
290
}
199
291
200
- /* pidfd from SCM_PIDFD should point to the parent process PID */
201
- parent_pid =
202
- get_pid_from_fdinfo_file (* pidfd , "Pid:" , sizeof ("Pid:" ) - 1 );
203
- if (parent_pid != getppid ()) {
204
- log_err ("wrong SCM_PIDFD %d != %d" , parent_pid , getppid ());
292
+ if (!(info .mask & PIDFD_INFO_EXIT )) {
293
+ log_err ("%s: No exit information from ioctl(PIDFD_GET_INFO)" , __func__ );
294
+ close (* res .pidfd );
205
295
return 1 ;
206
296
}
207
297
298
+ err = WIFEXITED (info .exit_code ) ? WEXITSTATUS (info .exit_code ) : 1 ;
299
+ if (err != CHILD_EXIT_CODE_OK ) {
300
+ log_err ("%s: wrong exit_code %d != %d" , __func__ , err , CHILD_EXIT_CODE_OK );
301
+ close (* res .pidfd );
302
+ return 1 ;
303
+ }
304
+
305
+ close (* res .pidfd );
208
306
return 0 ;
209
307
}
210
308
@@ -291,6 +389,24 @@ static void fill_sockaddr(struct sock_addr *addr, bool abstract)
291
389
memcpy (sun_path_buf , addr -> sock_name , strlen (addr -> sock_name ));
292
390
}
293
391
392
+ static int sk_enable_cred_pass (int sk )
393
+ {
394
+ int on = 0 ;
395
+
396
+ on = 1 ;
397
+ if (setsockopt (sk , SOL_SOCKET , SO_PASSCRED , & on , sizeof (on ))) {
398
+ log_err ("Failed to set SO_PASSCRED" );
399
+ return 1 ;
400
+ }
401
+
402
+ if (setsockopt (sk , SOL_SOCKET , SO_PASSPIDFD , & on , sizeof (on ))) {
403
+ log_err ("Failed to set SO_PASSPIDFD" );
404
+ return 1 ;
405
+ }
406
+
407
+ return 0 ;
408
+ }
409
+
294
410
static void client (FIXTURE_DATA (scm_pidfd ) * self ,
295
411
const FIXTURE_VARIANT (scm_pidfd ) * variant )
296
412
{
@@ -299,7 +415,6 @@ static void client(FIXTURE_DATA(scm_pidfd) *self,
299
415
struct ucred peer_cred ;
300
416
int peer_pidfd ;
301
417
pid_t peer_pid ;
302
- int on = 0 ;
303
418
304
419
cfd = socket (AF_UNIX , variant -> type , 0 );
305
420
if (cfd < 0 ) {
@@ -322,14 +437,8 @@ static void client(FIXTURE_DATA(scm_pidfd) *self,
322
437
child_die ();
323
438
}
324
439
325
- on = 1 ;
326
- if (setsockopt (cfd , SOL_SOCKET , SO_PASSCRED , & on , sizeof (on ))) {
327
- log_err ("Failed to set SO_PASSCRED" );
328
- child_die ();
329
- }
330
-
331
- if (setsockopt (cfd , SOL_SOCKET , SO_PASSPIDFD , & on , sizeof (on ))) {
332
- log_err ("Failed to set SO_PASSPIDFD" );
440
+ if (sk_enable_cred_pass (cfd )) {
441
+ log_err ("sk_enable_cred_pass() failed" );
333
442
child_die ();
334
443
}
335
444
@@ -340,6 +449,12 @@ static void client(FIXTURE_DATA(scm_pidfd) *self,
340
449
child_die ();
341
450
}
342
451
452
+ /* send something to the parent so it can receive SCM_PIDFD too and validate it */
453
+ if (send (cfd , "y" , sizeof (char ), 0 ) == -1 ) {
454
+ log_err ("Failed to send(cfd, \"y\", sizeof(char), 0)" );
455
+ child_die ();
456
+ }
457
+
343
458
/* skip further for SOCK_DGRAM as it's not applicable */
344
459
if (variant -> type == SOCK_DGRAM )
345
460
return ;
@@ -398,7 +513,13 @@ TEST_F(scm_pidfd, test)
398
513
close (self -> server );
399
514
close (self -> startup_pipe [0 ]);
400
515
client (self , variant );
401
- exit (0 );
516
+
517
+ /*
518
+ * It's a bit unusual, but in case of success we return non-zero
519
+ * exit code (CHILD_EXIT_CODE_OK) and then we expect to read it
520
+ * from ioctl(PIDFD_GET_INFO) in cmsg_check_dead().
521
+ */
522
+ exit (CHILD_EXIT_CODE_OK );
402
523
}
403
524
close (self -> startup_pipe [1 ]);
404
525
@@ -421,9 +542,17 @@ TEST_F(scm_pidfd, test)
421
542
ASSERT_NE (-1 , err );
422
543
}
423
544
424
- close (pfd );
425
545
waitpid (self -> client_pid , & child_status , 0 );
426
- ASSERT_EQ (0 , WIFEXITED (child_status ) ? WEXITSTATUS (child_status ) : 1 );
546
+ /* see comment before exit(CHILD_EXIT_CODE_OK) */
547
+ ASSERT_EQ (CHILD_EXIT_CODE_OK , WIFEXITED (child_status ) ? WEXITSTATUS (child_status ) : 1 );
548
+
549
+ err = sk_enable_cred_pass (pfd );
550
+ ASSERT_EQ (0 , err );
551
+
552
+ err = cmsg_check_dead (pfd , self -> client_pid );
553
+ ASSERT_EQ (0 , err );
554
+
555
+ close (pfd );
427
556
}
428
557
429
558
TEST_HARNESS_MAIN
0 commit comments