11 #include <sys/types.h>
12 #include <sys/capability.h>
14 #include <sys/syscall.h>
18 #include "kdbus-api.h"
19 #include "kdbus-util.h"
20 #include "kdbus-enum.h"
21 #include "kdbus-test.h"
23 int kdbus_test_hello(struct kdbus_test_env *env)
25 struct kdbus_cmd_free cmd_free = {};
26 struct kdbus_cmd_hello hello;
29 memset(&hello, 0, sizeof(hello));
31 fd = open(env->buspath, O_RDWR|O_CLOEXEC);
32 ASSERT_RETURN(fd >= 0);
34 hello.flags = KDBUS_HELLO_ACCEPT_FD;
35 hello.attach_flags_send = _KDBUS_ATTACH_ALL;
36 hello.attach_flags_recv = _KDBUS_ATTACH_ALL;
37 hello.size = sizeof(struct kdbus_cmd_hello);
38 hello.pool_size = POOL_SIZE;
40 /* an unaligned hello must result in -EFAULT */
41 ret = kdbus_cmd_hello(fd, (struct kdbus_cmd_hello *) ((char *) &hello + 1));
42 ASSERT_RETURN(ret == -EFAULT);
44 /* a size of 0 must return EMSGSIZE */
46 hello.flags = KDBUS_HELLO_ACCEPT_FD;
47 hello.attach_flags_send = _KDBUS_ATTACH_ALL;
48 ret = kdbus_cmd_hello(fd, &hello);
49 ASSERT_RETURN(ret == -EINVAL);
51 hello.size = sizeof(struct kdbus_cmd_hello);
53 /* check faulty flags */
54 hello.flags = 1ULL << 32;
55 hello.attach_flags_send = _KDBUS_ATTACH_ALL;
56 ret = kdbus_cmd_hello(fd, &hello);
57 ASSERT_RETURN(ret == -EINVAL);
59 /* check for faulty pool sizes */
61 hello.flags = KDBUS_HELLO_ACCEPT_FD;
62 hello.attach_flags_send = _KDBUS_ATTACH_ALL;
63 ret = kdbus_cmd_hello(fd, &hello);
64 ASSERT_RETURN(ret == -EINVAL);
66 hello.pool_size = 4097;
67 hello.attach_flags_send = _KDBUS_ATTACH_ALL;
68 ret = kdbus_cmd_hello(fd, &hello);
69 ASSERT_RETURN(ret == -EINVAL);
71 hello.pool_size = POOL_SIZE;
74 * The connection created by the core requires ALL meta flags
75 * to be sent. An attempt to send less than that should result in
78 hello.attach_flags_send = _KDBUS_ATTACH_ALL & ~KDBUS_ATTACH_TIMESTAMP;
79 ret = kdbus_cmd_hello(fd, &hello);
80 ASSERT_RETURN(ret == -ECONNREFUSED);
82 hello.attach_flags_send = _KDBUS_ATTACH_ALL;
83 hello.offset = (__u64)-1;
86 ret = kdbus_cmd_hello(fd, &hello);
87 ASSERT_RETURN(ret == 0);
89 /* The kernel should have returned some items */
90 ASSERT_RETURN(hello.offset != (__u64)-1);
91 cmd_free.size = sizeof(cmd_free);
92 cmd_free.offset = hello.offset;
93 ret = kdbus_cmd_free(fd, &cmd_free);
94 ASSERT_RETURN(ret >= 0);
98 fd = open(env->buspath, O_RDWR|O_CLOEXEC);
99 ASSERT_RETURN(fd >= 0);
101 /* no ACTIVATOR flag without a name */
102 hello.flags = KDBUS_HELLO_ACTIVATOR;
103 ret = kdbus_cmd_hello(fd, &hello);
104 ASSERT_RETURN(ret == -EINVAL);
111 int kdbus_test_byebye(struct kdbus_test_env *env)
113 struct kdbus_conn *conn;
114 struct kdbus_cmd_recv cmd_recv = { .size = sizeof(cmd_recv) };
115 struct kdbus_cmd cmd_byebye = { .size = sizeof(cmd_byebye) };
118 /* create a 2nd connection */
119 conn = kdbus_hello(env->buspath, 0, NULL, 0);
120 ASSERT_RETURN(conn != NULL);
122 ret = kdbus_add_match_empty(conn);
123 ASSERT_RETURN(ret == 0);
125 ret = kdbus_add_match_empty(env->conn);
126 ASSERT_RETURN(ret == 0);
128 /* send over 1st connection */
129 ret = kdbus_msg_send(env->conn, NULL, 0, 0, 0, 0,
130 KDBUS_DST_ID_BROADCAST, 0, NULL);
131 ASSERT_RETURN(ret == 0);
133 /* say byebye on the 2nd, which must fail */
134 ret = kdbus_cmd_byebye(conn->fd, &cmd_byebye);
135 ASSERT_RETURN(ret == -EBUSY);
137 /* receive the message */
138 ret = kdbus_cmd_recv(conn->fd, &cmd_recv);
139 ASSERT_RETURN(ret == 0);
141 ret = kdbus_free(conn, cmd_recv.msg.offset);
142 ASSERT_RETURN(ret == 0);
145 ret = kdbus_cmd_byebye(conn->fd, &cmd_byebye);
146 ASSERT_RETURN(ret == 0);
148 /* a 2nd try should result in -ECONNRESET */
149 ret = kdbus_cmd_byebye(conn->fd, &cmd_byebye);
150 ASSERT_RETURN(ret == -ECONNRESET);
152 kdbus_conn_free(conn);
157 /* Get only the first item */
158 static struct kdbus_item *kdbus_get_item(struct kdbus_info *info,
161 struct kdbus_item *item;
163 KDBUS_ITEM_FOREACH(item, info, items)
164 if (item->type == type)
170 static unsigned int kdbus_count_item(struct kdbus_info *info,
174 const struct kdbus_item *item;
176 KDBUS_ITEM_FOREACH(item, info, items)
177 if (item->type == type)
183 static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable)
186 unsigned int cnt = 0;
188 uint64_t kdbus_flags_mask;
189 struct kdbus_info *info;
190 struct kdbus_conn *conn;
191 struct kdbus_conn *privileged;
192 const struct kdbus_item *item;
193 uint64_t valid_flags_set;
194 uint64_t invalid_flags_set;
195 uint64_t valid_flags = KDBUS_ATTACH_NAMES |
198 KDBUS_ATTACH_CONN_DESCRIPTION;
200 uint64_t invalid_flags = KDBUS_ATTACH_NAMES |
204 KDBUS_ATTACH_CGROUP |
205 KDBUS_ATTACH_CONN_DESCRIPTION;
207 struct kdbus_creds cached_creds;
208 uid_t ruid, euid, suid;
209 gid_t rgid, egid, sgid;
211 getresuid(&ruid, &euid, &suid);
212 getresgid(&rgid, &egid, &sgid);
214 cached_creds.uid = ruid;
215 cached_creds.euid = euid;
216 cached_creds.suid = suid;
217 cached_creds.fsuid = ruid;
219 cached_creds.gid = rgid;
220 cached_creds.egid = egid;
221 cached_creds.sgid = sgid;
222 cached_creds.fsgid = rgid;
224 struct kdbus_pids cached_pids = {
226 .tid = syscall(SYS_gettid),
230 ret = kdbus_sysfs_get_parameter_mask(env->mask_param_path,
232 ASSERT_RETURN(ret == 0);
234 valid_flags_set = valid_flags & kdbus_flags_mask;
235 invalid_flags_set = invalid_flags & kdbus_flags_mask;
237 ret = kdbus_conn_info(env->conn, env->conn->id, NULL,
238 valid_flags, &offset);
239 ASSERT_RETURN(ret == 0);
241 info = (struct kdbus_info *)(env->conn->buf + offset);
242 ASSERT_RETURN(info->id == env->conn->id);
244 /* We do not have any well-known name */
245 item = kdbus_get_item(info, KDBUS_ITEM_NAME);
246 ASSERT_RETURN(item == NULL);
248 item = kdbus_get_item(info, KDBUS_ITEM_CONN_DESCRIPTION);
249 if (valid_flags_set & KDBUS_ATTACH_CONN_DESCRIPTION) {
252 ASSERT_RETURN(item == NULL);
255 kdbus_free(env->conn, offset);
257 conn = kdbus_hello(env->buspath, 0, NULL, 0);
260 privileged = kdbus_hello(env->buspath, 0, NULL, 0);
261 ASSERT_RETURN(privileged);
263 ret = kdbus_conn_info(conn, conn->id, NULL, valid_flags, &offset);
264 ASSERT_RETURN(ret == 0);
266 info = (struct kdbus_info *)(conn->buf + offset);
267 ASSERT_RETURN(info->id == conn->id);
269 /* We do not have any well-known name */
270 item = kdbus_get_item(info, KDBUS_ITEM_NAME);
271 ASSERT_RETURN(item == NULL);
273 cnt = kdbus_count_item(info, KDBUS_ITEM_CREDS);
274 if (valid_flags_set & KDBUS_ATTACH_CREDS) {
275 ASSERT_RETURN(cnt == 1);
277 item = kdbus_get_item(info, KDBUS_ITEM_CREDS);
280 /* Compare received items with cached creds */
281 ASSERT_RETURN(memcmp(&item->creds, &cached_creds,
282 sizeof(struct kdbus_creds)) == 0);
284 ASSERT_RETURN(cnt == 0);
287 item = kdbus_get_item(info, KDBUS_ITEM_PIDS);
288 if (valid_flags_set & KDBUS_ATTACH_PIDS) {
291 /* Compare item->pids with cached PIDs */
292 ASSERT_RETURN(item->pids.pid == cached_pids.pid &&
293 item->pids.tid == cached_pids.tid &&
294 item->pids.ppid == cached_pids.ppid);
296 ASSERT_RETURN(item == NULL);
299 /* We did not request KDBUS_ITEM_CAPS */
300 item = kdbus_get_item(info, KDBUS_ITEM_CAPS);
301 ASSERT_RETURN(item == NULL);
303 kdbus_free(conn, offset);
305 ret = kdbus_name_acquire(conn, "com.example.a", NULL);
306 ASSERT_RETURN(ret >= 0);
308 ret = kdbus_conn_info(conn, conn->id, NULL, valid_flags, &offset);
309 ASSERT_RETURN(ret == 0);
311 info = (struct kdbus_info *)(conn->buf + offset);
312 ASSERT_RETURN(info->id == conn->id);
314 item = kdbus_get_item(info, KDBUS_ITEM_OWNED_NAME);
315 if (valid_flags_set & KDBUS_ATTACH_NAMES) {
316 ASSERT_RETURN(item && !strcmp(item->name.name, "com.example.a"));
318 ASSERT_RETURN(item == NULL);
321 kdbus_free(conn, offset);
323 ret = kdbus_conn_info(conn, 0, "com.example.a", valid_flags, &offset);
324 ASSERT_RETURN(ret == 0);
326 info = (struct kdbus_info *)(conn->buf + offset);
327 ASSERT_RETURN(info->id == conn->id);
329 kdbus_free(conn, offset);
331 /* does not have the necessary caps to drop to unprivileged */
335 ret = RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_GID, ({
336 ret = kdbus_conn_info(conn, conn->id, NULL,
337 valid_flags, &offset);
338 ASSERT_EXIT(ret == 0);
340 info = (struct kdbus_info *)(conn->buf + offset);
341 ASSERT_EXIT(info->id == conn->id);
343 if (valid_flags_set & KDBUS_ATTACH_NAMES) {
344 item = kdbus_get_item(info, KDBUS_ITEM_OWNED_NAME);
346 strcmp(item->name.name,
347 "com.example.a") == 0);
350 if (valid_flags_set & KDBUS_ATTACH_CREDS) {
351 item = kdbus_get_item(info, KDBUS_ITEM_CREDS);
354 /* Compare received items with cached creds */
355 ASSERT_EXIT(memcmp(&item->creds, &cached_creds,
356 sizeof(struct kdbus_creds)) == 0);
359 if (valid_flags_set & KDBUS_ATTACH_PIDS) {
360 item = kdbus_get_item(info, KDBUS_ITEM_PIDS);
364 * Compare item->pids with cached pids of
367 * cmd_info will always return cached pids.
369 ASSERT_EXIT(item->pids.pid == cached_pids.pid &&
370 item->pids.tid == cached_pids.tid);
373 kdbus_free(conn, offset);
376 * Use invalid_flags and make sure that userspace
377 * do not play with us.
379 ret = kdbus_conn_info(conn, conn->id, NULL,
380 invalid_flags, &offset);
381 ASSERT_EXIT(ret == 0);
384 * Make sure that we return only one creds item and
385 * it points to the cached creds.
387 cnt = kdbus_count_item(info, KDBUS_ITEM_CREDS);
388 if (invalid_flags_set & KDBUS_ATTACH_CREDS) {
389 ASSERT_EXIT(cnt == 1);
391 item = kdbus_get_item(info, KDBUS_ITEM_CREDS);
394 /* Compare received items with cached creds */
395 ASSERT_EXIT(memcmp(&item->creds, &cached_creds,
396 sizeof(struct kdbus_creds)) == 0);
398 ASSERT_EXIT(cnt == 0);
401 if (invalid_flags_set & KDBUS_ATTACH_PIDS) {
402 cnt = kdbus_count_item(info, KDBUS_ITEM_PIDS);
403 ASSERT_EXIT(cnt == 1);
405 item = kdbus_get_item(info, KDBUS_ITEM_PIDS);
408 /* Compare item->pids with cached pids */
409 ASSERT_EXIT(item->pids.pid == cached_pids.pid &&
410 item->pids.tid == cached_pids.tid);
413 cnt = kdbus_count_item(info, KDBUS_ITEM_CGROUP);
414 if (invalid_flags_set & KDBUS_ATTACH_CGROUP) {
415 ASSERT_EXIT(cnt == 1);
417 ASSERT_EXIT(cnt == 0);
420 cnt = kdbus_count_item(info, KDBUS_ITEM_CAPS);
421 if (invalid_flags_set & KDBUS_ATTACH_CAPS) {
422 ASSERT_EXIT(cnt == 1);
424 ASSERT_EXIT(cnt == 0);
427 kdbus_free(conn, offset);
430 ASSERT_RETURN(ret == 0);
435 ret = kdbus_name_acquire(conn, "com.example.b", NULL);
436 ASSERT_RETURN(ret >= 0);
438 ret = kdbus_conn_info(conn, conn->id, NULL, valid_flags, &offset);
439 ASSERT_RETURN(ret == 0);
441 info = (struct kdbus_info *)(conn->buf + offset);
442 ASSERT_RETURN(info->id == conn->id);
444 cnt = kdbus_count_item(info, KDBUS_ITEM_OWNED_NAME);
445 if (valid_flags_set & KDBUS_ATTACH_NAMES) {
446 ASSERT_RETURN(cnt == 2);
448 ASSERT_RETURN(cnt == 0);
451 kdbus_free(conn, offset);
453 ASSERT_RETURN(ret == 0);
458 int kdbus_test_conn_info(struct kdbus_test_env *env)
463 struct kdbus_cmd_info cmd_info;
472 buf.cmd_info.size = sizeof(struct kdbus_cmd_info);
473 buf.cmd_info.flags = 0;
474 buf.cmd_info.attach_flags = 0;
475 buf.cmd_info.id = env->conn->id;
477 ret = kdbus_conn_info(env->conn, env->conn->id, NULL, 0, NULL);
478 ASSERT_RETURN(ret == 0);
480 /* try to pass a name that is longer than the buffer's size */
481 buf.name.size = KDBUS_ITEM_HEADER_SIZE + 1;
482 buf.name.type = KDBUS_ITEM_NAME;
483 strcpy(buf.name.str, "foo.bar.bla");
486 buf.cmd_info.size = sizeof(buf.cmd_info) + buf.name.size;
487 ret = kdbus_cmd_conn_info(env->conn->fd, (struct kdbus_cmd_info *) &buf);
488 ASSERT_RETURN(ret == -EINVAL);
490 /* Pass a non existent name */
491 ret = kdbus_conn_info(env->conn, 0, "non.existent.name", 0, NULL);
492 ASSERT_RETURN(ret == -ESRCH);
494 if (!all_uids_gids_are_mapped())
497 /* Test for caps here, so we run the previous test */
498 have_caps = test_is_capable(CAP_SETUID, CAP_SETGID, -1);
499 ASSERT_RETURN(have_caps >= 0);
501 ret = kdbus_fuzz_conn_info(env, have_caps);
502 ASSERT_RETURN(ret == 0);
504 /* Now if we have skipped some tests then let the user know */
511 int kdbus_test_conn_update(struct kdbus_test_env *env)
513 struct kdbus_conn *conn;
514 struct kdbus_msg *msg;
519 * kdbus_hello() sets all attach flags. Receive a message by this
520 * connection, and make sure a timestamp item (just to pick one) is
523 conn = kdbus_hello(env->buspath, 0, NULL, 0);
526 ret = kdbus_msg_send(env->conn, NULL, 0x12345678, 0, 0, 0, conn->id,
528 ASSERT_RETURN(ret == 0);
530 ret = kdbus_msg_recv(conn, &msg, NULL);
531 ASSERT_RETURN(ret == 0);
533 found = kdbus_item_in_message(msg, KDBUS_ITEM_TIMESTAMP);
534 ASSERT_RETURN(found == 1);
539 * Now, modify the attach flags and repeat the action. The item must
544 ret = kdbus_conn_update_attach_flags(conn,
547 ~KDBUS_ATTACH_TIMESTAMP);
548 ASSERT_RETURN(ret == 0);
550 ret = kdbus_msg_send(env->conn, NULL, 0x12345678, 0, 0, 0, conn->id,
552 ASSERT_RETURN(ret == 0);
554 ret = kdbus_msg_recv(conn, &msg, NULL);
555 ASSERT_RETURN(ret == 0);
557 found = kdbus_item_in_message(msg, KDBUS_ITEM_TIMESTAMP);
558 ASSERT_RETURN(found == 0);
560 /* Provide a bogus attach_flags value */
561 ret = kdbus_conn_update_attach_flags(conn,
562 _KDBUS_ATTACH_ALL + 1,
564 ASSERT_RETURN(ret == -EINVAL);
568 kdbus_conn_free(conn);
573 int kdbus_test_writable_pool(struct kdbus_test_env *env)
575 struct kdbus_cmd_free cmd_free = {};
576 struct kdbus_cmd_hello hello;
580 fd = open(env->buspath, O_RDWR | O_CLOEXEC);
581 ASSERT_RETURN(fd >= 0);
583 memset(&hello, 0, sizeof(hello));
584 hello.flags = KDBUS_HELLO_ACCEPT_FD;
585 hello.attach_flags_send = _KDBUS_ATTACH_ALL;
586 hello.attach_flags_recv = _KDBUS_ATTACH_ALL;
587 hello.size = sizeof(struct kdbus_cmd_hello);
588 hello.pool_size = POOL_SIZE;
589 hello.offset = (__u64)-1;
592 ret = kdbus_cmd_hello(fd, &hello);
593 ASSERT_RETURN(ret == 0);
595 /* The kernel should have returned some items */
596 ASSERT_RETURN(hello.offset != (__u64)-1);
597 cmd_free.size = sizeof(cmd_free);
598 cmd_free.offset = hello.offset;
599 ret = kdbus_cmd_free(fd, &cmd_free);
600 ASSERT_RETURN(ret >= 0);
602 /* pools cannot be mapped writable */
603 map = mmap(NULL, POOL_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
604 ASSERT_RETURN(map == MAP_FAILED);
606 /* pools can always be mapped readable */
607 map = mmap(NULL, POOL_SIZE, PROT_READ, MAP_SHARED, fd, 0);
608 ASSERT_RETURN(map != MAP_FAILED);
610 /* make sure we cannot change protection masks to writable */
611 ret = mprotect(map, POOL_SIZE, PROT_READ | PROT_WRITE);
612 ASSERT_RETURN(ret < 0);
614 munmap(map, POOL_SIZE);