1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2026 Christian Brauner <brauner@kernel.org>
3 /*
4 * Test extended attributes on path-based Unix domain sockets.
5 *
6 * Path-based Unix domain sockets are bound to a filesystem path and their
7 * inodes live on the underlying filesystem (e.g. tmpfs). These tests verify
8 * that user.* and trusted.* xattr operations work correctly on them using
9 * path-based syscalls (setxattr, getxattr, etc.).
10 *
11 * Covers SOCK_STREAM, SOCK_DGRAM, and SOCK_SEQPACKET socket types.
12 */
13
14 #define _GNU_SOURCE
15 #include <errno.h>
16 #include <limits.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/socket.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <sys/un.h>
24 #include <sys/xattr.h>
25 #include <unistd.h>
26
27 #include "../../kselftest_harness.h"
28
29 #define TEST_XATTR_NAME "user.testattr"
30 #define TEST_XATTR_VALUE "testvalue"
31 #define TEST_XATTR_VALUE2 "newvalue"
32
33 /*
34 * Fixture for path-based Unix domain socket tests.
35 * Creates a SOCK_STREAM socket bound to a path in /tmp (typically tmpfs).
36 */
FIXTURE(xattr_socket)37 FIXTURE(xattr_socket)
38 {
39 char socket_path[PATH_MAX];
40 int sockfd;
41 };
42
FIXTURE_VARIANT(xattr_socket)43 FIXTURE_VARIANT(xattr_socket)
44 {
45 int sock_type;
46 const char *name;
47 };
48
FIXTURE_VARIANT_ADD(xattr_socket,stream)49 FIXTURE_VARIANT_ADD(xattr_socket, stream) {
50 .sock_type = SOCK_STREAM,
51 .name = "stream",
52 };
53
FIXTURE_VARIANT_ADD(xattr_socket,dgram)54 FIXTURE_VARIANT_ADD(xattr_socket, dgram) {
55 .sock_type = SOCK_DGRAM,
56 .name = "dgram",
57 };
58
FIXTURE_VARIANT_ADD(xattr_socket,seqpacket)59 FIXTURE_VARIANT_ADD(xattr_socket, seqpacket) {
60 .sock_type = SOCK_SEQPACKET,
61 .name = "seqpacket",
62 };
63
FIXTURE_SETUP(xattr_socket)64 FIXTURE_SETUP(xattr_socket)
65 {
66 struct sockaddr_un addr;
67 int ret;
68
69 self->sockfd = -1;
70
71 snprintf(self->socket_path, sizeof(self->socket_path),
72 "/tmp/xattr_socket_test_%s.%d", variant->name, getpid());
73 unlink(self->socket_path);
74
75 self->sockfd = socket(AF_UNIX, variant->sock_type, 0);
76 ASSERT_GE(self->sockfd, 0) {
77 TH_LOG("Failed to create socket: %s", strerror(errno));
78 }
79
80 memset(&addr, 0, sizeof(addr));
81 addr.sun_family = AF_UNIX;
82 strncpy(addr.sun_path, self->socket_path, sizeof(addr.sun_path) - 1);
83
84 ret = bind(self->sockfd, (struct sockaddr *)&addr, sizeof(addr));
85 ASSERT_EQ(ret, 0) {
86 TH_LOG("Failed to bind socket to %s: %s",
87 self->socket_path, strerror(errno));
88 }
89 }
90
FIXTURE_TEARDOWN(xattr_socket)91 FIXTURE_TEARDOWN(xattr_socket)
92 {
93 if (self->sockfd >= 0)
94 close(self->sockfd);
95 unlink(self->socket_path);
96 }
97
TEST_F(xattr_socket,set_user_xattr)98 TEST_F(xattr_socket, set_user_xattr)
99 {
100 int ret;
101
102 ret = setxattr(self->socket_path, TEST_XATTR_NAME,
103 TEST_XATTR_VALUE, strlen(TEST_XATTR_VALUE), 0);
104 ASSERT_EQ(ret, 0) {
105 TH_LOG("setxattr failed: %s (errno=%d)", strerror(errno), errno);
106 }
107 }
108
TEST_F(xattr_socket,get_user_xattr)109 TEST_F(xattr_socket, get_user_xattr)
110 {
111 char buf[256];
112 ssize_t ret;
113
114 ret = setxattr(self->socket_path, TEST_XATTR_NAME,
115 TEST_XATTR_VALUE, strlen(TEST_XATTR_VALUE), 0);
116 ASSERT_EQ(ret, 0) {
117 TH_LOG("setxattr failed: %s", strerror(errno));
118 }
119
120 memset(buf, 0, sizeof(buf));
121 ret = getxattr(self->socket_path, TEST_XATTR_NAME, buf, sizeof(buf));
122 ASSERT_EQ(ret, (ssize_t)strlen(TEST_XATTR_VALUE)) {
123 TH_LOG("getxattr returned %zd, expected %zu: %s",
124 ret, strlen(TEST_XATTR_VALUE), strerror(errno));
125 }
126 ASSERT_STREQ(buf, TEST_XATTR_VALUE);
127 }
128
TEST_F(xattr_socket,list_user_xattr)129 TEST_F(xattr_socket, list_user_xattr)
130 {
131 char list[1024];
132 ssize_t ret;
133 bool found = false;
134 char *ptr;
135
136 ret = setxattr(self->socket_path, TEST_XATTR_NAME,
137 TEST_XATTR_VALUE, strlen(TEST_XATTR_VALUE), 0);
138 ASSERT_EQ(ret, 0) {
139 TH_LOG("setxattr failed: %s", strerror(errno));
140 }
141
142 memset(list, 0, sizeof(list));
143 ret = listxattr(self->socket_path, list, sizeof(list));
144 ASSERT_GT(ret, 0) {
145 TH_LOG("listxattr failed: %s", strerror(errno));
146 }
147
148 for (ptr = list; ptr < list + ret; ptr += strlen(ptr) + 1) {
149 if (strcmp(ptr, TEST_XATTR_NAME) == 0) {
150 found = true;
151 break;
152 }
153 }
154 ASSERT_TRUE(found) {
155 TH_LOG("xattr %s not found in list", TEST_XATTR_NAME);
156 }
157 }
158
TEST_F(xattr_socket,remove_user_xattr)159 TEST_F(xattr_socket, remove_user_xattr)
160 {
161 char buf[256];
162 ssize_t ret;
163
164 ret = setxattr(self->socket_path, TEST_XATTR_NAME,
165 TEST_XATTR_VALUE, strlen(TEST_XATTR_VALUE), 0);
166 ASSERT_EQ(ret, 0) {
167 TH_LOG("setxattr failed: %s", strerror(errno));
168 }
169
170 ret = removexattr(self->socket_path, TEST_XATTR_NAME);
171 ASSERT_EQ(ret, 0) {
172 TH_LOG("removexattr failed: %s", strerror(errno));
173 }
174
175 ret = getxattr(self->socket_path, TEST_XATTR_NAME, buf, sizeof(buf));
176 ASSERT_EQ(ret, -1);
177 ASSERT_EQ(errno, ENODATA) {
178 TH_LOG("Expected ENODATA, got %s", strerror(errno));
179 }
180 }
181
182 /*
183 * Test that xattrs persist across socket close and reopen.
184 * The xattr is on the filesystem inode, not the socket fd.
185 */
TEST_F(xattr_socket,xattr_persistence)186 TEST_F(xattr_socket, xattr_persistence)
187 {
188 char buf[256];
189 ssize_t ret;
190
191 ret = setxattr(self->socket_path, TEST_XATTR_NAME,
192 TEST_XATTR_VALUE, strlen(TEST_XATTR_VALUE), 0);
193 ASSERT_EQ(ret, 0) {
194 TH_LOG("setxattr failed: %s", strerror(errno));
195 }
196
197 close(self->sockfd);
198 self->sockfd = -1;
199
200 memset(buf, 0, sizeof(buf));
201 ret = getxattr(self->socket_path, TEST_XATTR_NAME, buf, sizeof(buf));
202 ASSERT_EQ(ret, (ssize_t)strlen(TEST_XATTR_VALUE)) {
203 TH_LOG("getxattr after close failed: %s", strerror(errno));
204 }
205 ASSERT_STREQ(buf, TEST_XATTR_VALUE);
206 }
207
TEST_F(xattr_socket,update_user_xattr)208 TEST_F(xattr_socket, update_user_xattr)
209 {
210 char buf[256];
211 ssize_t ret;
212
213 ret = setxattr(self->socket_path, TEST_XATTR_NAME,
214 TEST_XATTR_VALUE, strlen(TEST_XATTR_VALUE), 0);
215 ASSERT_EQ(ret, 0);
216
217 ret = setxattr(self->socket_path, TEST_XATTR_NAME,
218 TEST_XATTR_VALUE2, strlen(TEST_XATTR_VALUE2), 0);
219 ASSERT_EQ(ret, 0);
220
221 memset(buf, 0, sizeof(buf));
222 ret = getxattr(self->socket_path, TEST_XATTR_NAME, buf, sizeof(buf));
223 ASSERT_EQ(ret, (ssize_t)strlen(TEST_XATTR_VALUE2));
224 ASSERT_STREQ(buf, TEST_XATTR_VALUE2);
225 }
226
TEST_F(xattr_socket,xattr_create_flag)227 TEST_F(xattr_socket, xattr_create_flag)
228 {
229 int ret;
230
231 ret = setxattr(self->socket_path, TEST_XATTR_NAME,
232 TEST_XATTR_VALUE, strlen(TEST_XATTR_VALUE), 0);
233 ASSERT_EQ(ret, 0);
234
235 ret = setxattr(self->socket_path, TEST_XATTR_NAME,
236 TEST_XATTR_VALUE2, strlen(TEST_XATTR_VALUE2), XATTR_CREATE);
237 ASSERT_EQ(ret, -1);
238 ASSERT_EQ(errno, EEXIST);
239 }
240
TEST_F(xattr_socket,xattr_replace_flag)241 TEST_F(xattr_socket, xattr_replace_flag)
242 {
243 int ret;
244
245 ret = setxattr(self->socket_path, TEST_XATTR_NAME,
246 TEST_XATTR_VALUE, strlen(TEST_XATTR_VALUE), XATTR_REPLACE);
247 ASSERT_EQ(ret, -1);
248 ASSERT_EQ(errno, ENODATA);
249 }
250
TEST_F(xattr_socket,multiple_xattrs)251 TEST_F(xattr_socket, multiple_xattrs)
252 {
253 char buf[256];
254 ssize_t ret;
255 int i;
256 char name[64], value[64];
257 const int num_xattrs = 5;
258
259 for (i = 0; i < num_xattrs; i++) {
260 snprintf(name, sizeof(name), "user.test%d", i);
261 snprintf(value, sizeof(value), "value%d", i);
262 ret = setxattr(self->socket_path, name, value, strlen(value), 0);
263 ASSERT_EQ(ret, 0) {
264 TH_LOG("setxattr %s failed: %s", name, strerror(errno));
265 }
266 }
267
268 for (i = 0; i < num_xattrs; i++) {
269 snprintf(name, sizeof(name), "user.test%d", i);
270 snprintf(value, sizeof(value), "value%d", i);
271 memset(buf, 0, sizeof(buf));
272 ret = getxattr(self->socket_path, name, buf, sizeof(buf));
273 ASSERT_EQ(ret, (ssize_t)strlen(value));
274 ASSERT_STREQ(buf, value);
275 }
276 }
277
TEST_F(xattr_socket,xattr_empty_value)278 TEST_F(xattr_socket, xattr_empty_value)
279 {
280 char buf[256];
281 ssize_t ret;
282
283 ret = setxattr(self->socket_path, TEST_XATTR_NAME, "", 0, 0);
284 ASSERT_EQ(ret, 0);
285
286 ret = getxattr(self->socket_path, TEST_XATTR_NAME, buf, sizeof(buf));
287 ASSERT_EQ(ret, 0);
288 }
289
TEST_F(xattr_socket,xattr_get_size)290 TEST_F(xattr_socket, xattr_get_size)
291 {
292 ssize_t ret;
293
294 ret = setxattr(self->socket_path, TEST_XATTR_NAME,
295 TEST_XATTR_VALUE, strlen(TEST_XATTR_VALUE), 0);
296 ASSERT_EQ(ret, 0);
297
298 ret = getxattr(self->socket_path, TEST_XATTR_NAME, NULL, 0);
299 ASSERT_EQ(ret, (ssize_t)strlen(TEST_XATTR_VALUE));
300 }
301
TEST_F(xattr_socket,xattr_buffer_too_small)302 TEST_F(xattr_socket, xattr_buffer_too_small)
303 {
304 char buf[2];
305 ssize_t ret;
306
307 ret = setxattr(self->socket_path, TEST_XATTR_NAME,
308 TEST_XATTR_VALUE, strlen(TEST_XATTR_VALUE), 0);
309 ASSERT_EQ(ret, 0);
310
311 ret = getxattr(self->socket_path, TEST_XATTR_NAME, buf, sizeof(buf));
312 ASSERT_EQ(ret, -1);
313 ASSERT_EQ(errno, ERANGE);
314 }
315
TEST_F(xattr_socket,xattr_nonexistent)316 TEST_F(xattr_socket, xattr_nonexistent)
317 {
318 char buf[256];
319 ssize_t ret;
320
321 ret = getxattr(self->socket_path, "user.nonexistent", buf, sizeof(buf));
322 ASSERT_EQ(ret, -1);
323 ASSERT_EQ(errno, ENODATA);
324 }
325
TEST_F(xattr_socket,remove_nonexistent_xattr)326 TEST_F(xattr_socket, remove_nonexistent_xattr)
327 {
328 int ret;
329
330 ret = removexattr(self->socket_path, "user.nonexistent");
331 ASSERT_EQ(ret, -1);
332 ASSERT_EQ(errno, ENODATA);
333 }
334
TEST_F(xattr_socket,large_xattr_value)335 TEST_F(xattr_socket, large_xattr_value)
336 {
337 char large_value[4096];
338 char read_buf[4096];
339 ssize_t ret;
340
341 memset(large_value, 'A', sizeof(large_value));
342
343 ret = setxattr(self->socket_path, TEST_XATTR_NAME,
344 large_value, sizeof(large_value), 0);
345 ASSERT_EQ(ret, 0) {
346 TH_LOG("setxattr with large value failed: %s", strerror(errno));
347 }
348
349 memset(read_buf, 0, sizeof(read_buf));
350 ret = getxattr(self->socket_path, TEST_XATTR_NAME,
351 read_buf, sizeof(read_buf));
352 ASSERT_EQ(ret, (ssize_t)sizeof(large_value));
353 ASSERT_EQ(memcmp(large_value, read_buf, sizeof(large_value)), 0);
354 }
355
356 /*
357 * Test lsetxattr/lgetxattr (don't follow symlinks).
358 * Socket files aren't symlinks, so this should work the same.
359 */
TEST_F(xattr_socket,lsetxattr_lgetxattr)360 TEST_F(xattr_socket, lsetxattr_lgetxattr)
361 {
362 char buf[256];
363 ssize_t ret;
364
365 ret = lsetxattr(self->socket_path, TEST_XATTR_NAME,
366 TEST_XATTR_VALUE, strlen(TEST_XATTR_VALUE), 0);
367 ASSERT_EQ(ret, 0) {
368 TH_LOG("lsetxattr failed: %s", strerror(errno));
369 }
370
371 memset(buf, 0, sizeof(buf));
372 ret = lgetxattr(self->socket_path, TEST_XATTR_NAME, buf, sizeof(buf));
373 ASSERT_EQ(ret, (ssize_t)strlen(TEST_XATTR_VALUE));
374 ASSERT_STREQ(buf, TEST_XATTR_VALUE);
375 }
376
377 /*
378 * Fixture for trusted.* xattr tests.
379 * These require CAP_SYS_ADMIN.
380 */
FIXTURE(xattr_socket_trusted)381 FIXTURE(xattr_socket_trusted)
382 {
383 char socket_path[PATH_MAX];
384 int sockfd;
385 };
386
FIXTURE_VARIANT(xattr_socket_trusted)387 FIXTURE_VARIANT(xattr_socket_trusted)
388 {
389 int sock_type;
390 const char *name;
391 };
392
FIXTURE_VARIANT_ADD(xattr_socket_trusted,stream)393 FIXTURE_VARIANT_ADD(xattr_socket_trusted, stream) {
394 .sock_type = SOCK_STREAM,
395 .name = "stream",
396 };
397
FIXTURE_VARIANT_ADD(xattr_socket_trusted,dgram)398 FIXTURE_VARIANT_ADD(xattr_socket_trusted, dgram) {
399 .sock_type = SOCK_DGRAM,
400 .name = "dgram",
401 };
402
FIXTURE_VARIANT_ADD(xattr_socket_trusted,seqpacket)403 FIXTURE_VARIANT_ADD(xattr_socket_trusted, seqpacket) {
404 .sock_type = SOCK_SEQPACKET,
405 .name = "seqpacket",
406 };
407
FIXTURE_SETUP(xattr_socket_trusted)408 FIXTURE_SETUP(xattr_socket_trusted)
409 {
410 struct sockaddr_un addr;
411 int ret;
412
413 self->sockfd = -1;
414
415 snprintf(self->socket_path, sizeof(self->socket_path),
416 "/tmp/xattr_socket_trusted_%s.%d", variant->name, getpid());
417 unlink(self->socket_path);
418
419 self->sockfd = socket(AF_UNIX, variant->sock_type, 0);
420 ASSERT_GE(self->sockfd, 0);
421
422 memset(&addr, 0, sizeof(addr));
423 addr.sun_family = AF_UNIX;
424 strncpy(addr.sun_path, self->socket_path, sizeof(addr.sun_path) - 1);
425
426 ret = bind(self->sockfd, (struct sockaddr *)&addr, sizeof(addr));
427 ASSERT_EQ(ret, 0);
428 }
429
FIXTURE_TEARDOWN(xattr_socket_trusted)430 FIXTURE_TEARDOWN(xattr_socket_trusted)
431 {
432 if (self->sockfd >= 0)
433 close(self->sockfd);
434 unlink(self->socket_path);
435 }
436
TEST_F(xattr_socket_trusted,set_trusted_xattr)437 TEST_F(xattr_socket_trusted, set_trusted_xattr)
438 {
439 char buf[256];
440 ssize_t len;
441 int ret;
442
443 ret = setxattr(self->socket_path, "trusted.testattr",
444 TEST_XATTR_VALUE, strlen(TEST_XATTR_VALUE), 0);
445 if (ret == -1 && errno == EPERM)
446 SKIP(return, "Need CAP_SYS_ADMIN for trusted.* xattrs");
447 ASSERT_EQ(ret, 0) {
448 TH_LOG("setxattr trusted.testattr failed: %s", strerror(errno));
449 }
450
451 memset(buf, 0, sizeof(buf));
452 len = getxattr(self->socket_path, "trusted.testattr",
453 buf, sizeof(buf));
454 ASSERT_EQ(len, (ssize_t)strlen(TEST_XATTR_VALUE));
455 ASSERT_STREQ(buf, TEST_XATTR_VALUE);
456 }
457
TEST_F(xattr_socket_trusted,get_trusted_xattr_unprivileged)458 TEST_F(xattr_socket_trusted, get_trusted_xattr_unprivileged)
459 {
460 char buf[256];
461 ssize_t ret;
462
463 ret = getxattr(self->socket_path, "trusted.testattr", buf, sizeof(buf));
464 ASSERT_EQ(ret, -1);
465 ASSERT_TRUE(errno == ENODATA || errno == EPERM) {
466 TH_LOG("Expected ENODATA or EPERM, got %s", strerror(errno));
467 }
468 }
469
470 TEST_HARNESS_MAIN
471