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 */ 37 FIXTURE(xattr_socket) 38 { 39 char socket_path[PATH_MAX]; 40 int sockfd; 41 }; 42 43 FIXTURE_VARIANT(xattr_socket) 44 { 45 int sock_type; 46 const char *name; 47 }; 48 49 FIXTURE_VARIANT_ADD(xattr_socket, stream) { 50 .sock_type = SOCK_STREAM, 51 .name = "stream", 52 }; 53 54 FIXTURE_VARIANT_ADD(xattr_socket, dgram) { 55 .sock_type = SOCK_DGRAM, 56 .name = "dgram", 57 }; 58 59 FIXTURE_VARIANT_ADD(xattr_socket, seqpacket) { 60 .sock_type = SOCK_SEQPACKET, 61 .name = "seqpacket", 62 }; 63 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 91 FIXTURE_TEARDOWN(xattr_socket) 92 { 93 if (self->sockfd >= 0) 94 close(self->sockfd); 95 unlink(self->socket_path); 96 } 97 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 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 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 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 */ 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 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 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 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 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 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 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 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 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 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 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 */ 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 */ 381 FIXTURE(xattr_socket_trusted) 382 { 383 char socket_path[PATH_MAX]; 384 int sockfd; 385 }; 386 387 FIXTURE_VARIANT(xattr_socket_trusted) 388 { 389 int sock_type; 390 const char *name; 391 }; 392 393 FIXTURE_VARIANT_ADD(xattr_socket_trusted, stream) { 394 .sock_type = SOCK_STREAM, 395 .name = "stream", 396 }; 397 398 FIXTURE_VARIANT_ADD(xattr_socket_trusted, dgram) { 399 .sock_type = SOCK_DGRAM, 400 .name = "dgram", 401 }; 402 403 FIXTURE_VARIANT_ADD(xattr_socket_trusted, seqpacket) { 404 .sock_type = SOCK_SEQPACKET, 405 .name = "seqpacket", 406 }; 407 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 430 FIXTURE_TEARDOWN(xattr_socket_trusted) 431 { 432 if (self->sockfd >= 0) 433 close(self->sockfd); 434 unlink(self->socket_path); 435 } 436 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 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