xref: /linux/tools/testing/selftests/filesystems/xattr/xattr_socket_test.c (revision c8db08110cbeff12a1f3990a31730936b092f62b)
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