1 // SPDX-License-Identifier: GPL-2.0 2 3 /* 4 * Copyright (c) 2025, Google LLC. 5 * Pasha Tatashin <pasha.tatashin@soleen.com> 6 */ 7 8 /* 9 * Selftests for the Live Update Orchestrator. 10 * This test suite verifies the functionality and behavior of the 11 * /dev/liveupdate character device and its session management capabilities. 12 * 13 * Tests include: 14 * - Device access: basic open/close, and enforcement of exclusive access. 15 * - Session management: creation of unique sessions, and duplicate name detection. 16 * - Resource preservation: successfully preserving individual and multiple memfds, 17 * verifying contents remain accessible. 18 * - Complex multi-session scenarios involving mixed empty and populated files. 19 */ 20 21 #include <errno.h> 22 #include <fcntl.h> 23 #include <string.h> 24 #include <sys/ioctl.h> 25 #include <unistd.h> 26 27 #include <linux/liveupdate.h> 28 29 #include "luo_test_utils.h" 30 #include "../kselftest.h" 31 #include "../kselftest_harness.h" 32 33 #define LIVEUPDATE_DEV "/dev/liveupdate" 34 35 FIXTURE(liveupdate_device) { 36 int fd1; 37 int fd2; 38 }; 39 40 FIXTURE_SETUP(liveupdate_device) 41 { 42 self->fd1 = -1; 43 self->fd2 = -1; 44 } 45 46 FIXTURE_TEARDOWN(liveupdate_device) 47 { 48 if (self->fd1 >= 0) 49 close(self->fd1); 50 if (self->fd2 >= 0) 51 close(self->fd2); 52 } 53 54 /* 55 * Test Case: Basic Open and Close 56 * 57 * Verifies that the /dev/liveupdate device can be opened and subsequently 58 * closed without errors. Skips if the device does not exist. 59 */ 60 TEST_F(liveupdate_device, basic_open_close) 61 { 62 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 63 64 if (self->fd1 < 0 && errno == ENOENT) 65 SKIP(return, "%s does not exist.", LIVEUPDATE_DEV); 66 67 ASSERT_GE(self->fd1, 0); 68 ASSERT_EQ(close(self->fd1), 0); 69 self->fd1 = -1; 70 } 71 72 /* 73 * Test Case: Exclusive Open Enforcement 74 * 75 * Verifies that the /dev/liveupdate device can only be opened by one process 76 * at a time. It checks that a second attempt to open the device fails with 77 * the EBUSY error code. 78 */ 79 TEST_F(liveupdate_device, exclusive_open) 80 { 81 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 82 83 if (self->fd1 < 0 && errno == ENOENT) 84 SKIP(return, "%s does not exist.", LIVEUPDATE_DEV); 85 86 ASSERT_GE(self->fd1, 0); 87 self->fd2 = open(LIVEUPDATE_DEV, O_RDWR); 88 EXPECT_LT(self->fd2, 0); 89 EXPECT_EQ(errno, EBUSY); 90 } 91 92 /* Helper function to create a LUO session via ioctl. */ 93 static int create_session(int lu_fd, const char *name) 94 { 95 struct liveupdate_ioctl_create_session args = {}; 96 97 args.size = sizeof(args); 98 strncpy((char *)args.name, name, sizeof(args.name) - 1); 99 100 if (ioctl(lu_fd, LIVEUPDATE_IOCTL_CREATE_SESSION, &args)) 101 return -errno; 102 103 return args.fd; 104 } 105 106 /* Helper function to get a session name via ioctl. */ 107 static int get_session_name(int session_fd, char *name, size_t name_len) 108 { 109 struct liveupdate_session_get_name args = {}; 110 111 args.size = sizeof(args); 112 113 if (ioctl(session_fd, LIVEUPDATE_SESSION_GET_NAME, &args)) 114 return -errno; 115 116 strncpy(name, (char *)args.name, name_len - 1); 117 name[name_len - 1] = '\0'; 118 119 return 0; 120 } 121 122 /* 123 * Test Case: Create Duplicate Session 124 * 125 * Verifies that attempting to create two sessions with the same name fails 126 * on the second attempt with EEXIST. 127 */ 128 TEST_F(liveupdate_device, create_duplicate_session) 129 { 130 int session_fd1, session_fd2; 131 132 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 133 if (self->fd1 < 0 && errno == ENOENT) 134 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 135 136 ASSERT_GE(self->fd1, 0); 137 138 session_fd1 = create_session(self->fd1, "duplicate-session-test"); 139 ASSERT_GE(session_fd1, 0); 140 141 session_fd2 = create_session(self->fd1, "duplicate-session-test"); 142 EXPECT_LT(session_fd2, 0); 143 EXPECT_EQ(-session_fd2, EEXIST); 144 145 ASSERT_EQ(close(session_fd1), 0); 146 } 147 148 /* 149 * Test Case: Create Distinct Sessions 150 * 151 * Verifies that creating two sessions with different names succeeds. 152 */ 153 TEST_F(liveupdate_device, create_distinct_sessions) 154 { 155 int session_fd1, session_fd2; 156 157 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 158 if (self->fd1 < 0 && errno == ENOENT) 159 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 160 161 ASSERT_GE(self->fd1, 0); 162 163 session_fd1 = create_session(self->fd1, "distinct-session-1"); 164 ASSERT_GE(session_fd1, 0); 165 166 session_fd2 = create_session(self->fd1, "distinct-session-2"); 167 ASSERT_GE(session_fd2, 0); 168 169 ASSERT_EQ(close(session_fd1), 0); 170 ASSERT_EQ(close(session_fd2), 0); 171 } 172 173 static int preserve_fd(int session_fd, int fd_to_preserve, __u64 token) 174 { 175 struct liveupdate_session_preserve_fd args = {}; 176 177 args.size = sizeof(args); 178 args.fd = fd_to_preserve; 179 args.token = token; 180 181 if (ioctl(session_fd, LIVEUPDATE_SESSION_PRESERVE_FD, &args)) 182 return -errno; 183 184 return 0; 185 } 186 187 /* 188 * Test Case: Preserve MemFD 189 * 190 * Verifies that a valid memfd can be successfully preserved in a session and 191 * that its contents remain intact after the preservation call. 192 */ 193 TEST_F(liveupdate_device, preserve_memfd) 194 { 195 const char *test_str = "hello liveupdate"; 196 char read_buf[64] = {}; 197 int session_fd, mem_fd; 198 199 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 200 if (self->fd1 < 0 && errno == ENOENT) 201 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 202 ASSERT_GE(self->fd1, 0); 203 204 session_fd = create_session(self->fd1, "preserve-memfd-test"); 205 ASSERT_GE(session_fd, 0); 206 207 mem_fd = memfd_create("test-memfd", 0); 208 ASSERT_GE(mem_fd, 0); 209 210 ASSERT_EQ(write(mem_fd, test_str, strlen(test_str)), strlen(test_str)); 211 ASSERT_EQ(preserve_fd(session_fd, mem_fd, 0x1234), 0); 212 ASSERT_EQ(close(session_fd), 0); 213 214 ASSERT_EQ(lseek(mem_fd, 0, SEEK_SET), 0); 215 ASSERT_EQ(read(mem_fd, read_buf, sizeof(read_buf)), strlen(test_str)); 216 ASSERT_STREQ(read_buf, test_str); 217 ASSERT_EQ(close(mem_fd), 0); 218 } 219 220 /* 221 * Test Case: Preserve Multiple MemFDs 222 * 223 * Verifies that multiple memfds can be preserved in a single session, 224 * each with a unique token, and that their contents remain distinct and 225 * correct after preservation. 226 */ 227 TEST_F(liveupdate_device, preserve_multiple_memfds) 228 { 229 const char *test_str1 = "data for memfd one"; 230 const char *test_str2 = "data for memfd two"; 231 char read_buf[64] = {}; 232 int session_fd, mem_fd1, mem_fd2; 233 234 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 235 if (self->fd1 < 0 && errno == ENOENT) 236 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 237 ASSERT_GE(self->fd1, 0); 238 239 session_fd = create_session(self->fd1, "preserve-multi-memfd-test"); 240 ASSERT_GE(session_fd, 0); 241 242 mem_fd1 = memfd_create("test-memfd-1", 0); 243 ASSERT_GE(mem_fd1, 0); 244 mem_fd2 = memfd_create("test-memfd-2", 0); 245 ASSERT_GE(mem_fd2, 0); 246 247 ASSERT_EQ(write(mem_fd1, test_str1, strlen(test_str1)), strlen(test_str1)); 248 ASSERT_EQ(write(mem_fd2, test_str2, strlen(test_str2)), strlen(test_str2)); 249 250 ASSERT_EQ(preserve_fd(session_fd, mem_fd1, 0xAAAA), 0); 251 ASSERT_EQ(preserve_fd(session_fd, mem_fd2, 0xBBBB), 0); 252 253 memset(read_buf, 0, sizeof(read_buf)); 254 ASSERT_EQ(lseek(mem_fd1, 0, SEEK_SET), 0); 255 ASSERT_EQ(read(mem_fd1, read_buf, sizeof(read_buf)), strlen(test_str1)); 256 ASSERT_STREQ(read_buf, test_str1); 257 258 memset(read_buf, 0, sizeof(read_buf)); 259 ASSERT_EQ(lseek(mem_fd2, 0, SEEK_SET), 0); 260 ASSERT_EQ(read(mem_fd2, read_buf, sizeof(read_buf)), strlen(test_str2)); 261 ASSERT_STREQ(read_buf, test_str2); 262 263 ASSERT_EQ(close(mem_fd1), 0); 264 ASSERT_EQ(close(mem_fd2), 0); 265 ASSERT_EQ(close(session_fd), 0); 266 } 267 268 /* 269 * Test Case: Preserve Complex Scenario 270 * 271 * Verifies a more complex scenario with multiple sessions and a mix of empty 272 * and non-empty memfds distributed across them. 273 */ 274 TEST_F(liveupdate_device, preserve_complex_scenario) 275 { 276 const char *data1 = "data for session 1"; 277 const char *data2 = "data for session 2"; 278 char read_buf[64] = {}; 279 int session_fd1, session_fd2; 280 int mem_fd_data1, mem_fd_empty1, mem_fd_data2, mem_fd_empty2; 281 282 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 283 if (self->fd1 < 0 && errno == ENOENT) 284 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 285 ASSERT_GE(self->fd1, 0); 286 287 session_fd1 = create_session(self->fd1, "complex-session-1"); 288 ASSERT_GE(session_fd1, 0); 289 session_fd2 = create_session(self->fd1, "complex-session-2"); 290 ASSERT_GE(session_fd2, 0); 291 292 mem_fd_data1 = memfd_create("data1", 0); 293 ASSERT_GE(mem_fd_data1, 0); 294 ASSERT_EQ(write(mem_fd_data1, data1, strlen(data1)), strlen(data1)); 295 296 mem_fd_empty1 = memfd_create("empty1", 0); 297 ASSERT_GE(mem_fd_empty1, 0); 298 299 mem_fd_data2 = memfd_create("data2", 0); 300 ASSERT_GE(mem_fd_data2, 0); 301 ASSERT_EQ(write(mem_fd_data2, data2, strlen(data2)), strlen(data2)); 302 303 mem_fd_empty2 = memfd_create("empty2", 0); 304 ASSERT_GE(mem_fd_empty2, 0); 305 306 ASSERT_EQ(preserve_fd(session_fd1, mem_fd_data1, 0x1111), 0); 307 ASSERT_EQ(preserve_fd(session_fd1, mem_fd_empty1, 0x2222), 0); 308 ASSERT_EQ(preserve_fd(session_fd2, mem_fd_data2, 0x3333), 0); 309 ASSERT_EQ(preserve_fd(session_fd2, mem_fd_empty2, 0x4444), 0); 310 311 ASSERT_EQ(lseek(mem_fd_data1, 0, SEEK_SET), 0); 312 ASSERT_EQ(read(mem_fd_data1, read_buf, sizeof(read_buf)), strlen(data1)); 313 ASSERT_STREQ(read_buf, data1); 314 315 memset(read_buf, 0, sizeof(read_buf)); 316 ASSERT_EQ(lseek(mem_fd_data2, 0, SEEK_SET), 0); 317 ASSERT_EQ(read(mem_fd_data2, read_buf, sizeof(read_buf)), strlen(data2)); 318 ASSERT_STREQ(read_buf, data2); 319 320 ASSERT_EQ(lseek(mem_fd_empty1, 0, SEEK_SET), 0); 321 ASSERT_EQ(read(mem_fd_empty1, read_buf, sizeof(read_buf)), 0); 322 323 ASSERT_EQ(lseek(mem_fd_empty2, 0, SEEK_SET), 0); 324 ASSERT_EQ(read(mem_fd_empty2, read_buf, sizeof(read_buf)), 0); 325 326 ASSERT_EQ(close(mem_fd_data1), 0); 327 ASSERT_EQ(close(mem_fd_empty1), 0); 328 ASSERT_EQ(close(mem_fd_data2), 0); 329 ASSERT_EQ(close(mem_fd_empty2), 0); 330 ASSERT_EQ(close(session_fd1), 0); 331 ASSERT_EQ(close(session_fd2), 0); 332 } 333 334 /* 335 * Test Case: Preserve Unsupported File Descriptor 336 * 337 * Verifies that attempting to preserve a file descriptor that does not have 338 * a registered Live Update handler fails gracefully. 339 * Uses /dev/null as a representative of a file type (character device) 340 * that is not supported by the orchestrator. 341 */ 342 TEST_F(liveupdate_device, preserve_unsupported_fd) 343 { 344 int session_fd, unsupported_fd; 345 int ret; 346 347 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 348 if (self->fd1 < 0 && errno == ENOENT) 349 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 350 ASSERT_GE(self->fd1, 0); 351 352 session_fd = create_session(self->fd1, "unsupported-fd-test"); 353 ASSERT_GE(session_fd, 0); 354 355 unsupported_fd = open("/dev/null", O_RDWR); 356 ASSERT_GE(unsupported_fd, 0); 357 358 ret = preserve_fd(session_fd, unsupported_fd, 0xDEAD); 359 EXPECT_EQ(ret, -ENOENT); 360 361 ASSERT_EQ(close(unsupported_fd), 0); 362 ASSERT_EQ(close(session_fd), 0); 363 } 364 365 /* 366 * Test Case: Prevent Double Preservation 367 * 368 * Verifies that a file (memfd) can only be preserved once across all active 369 * sessions. Attempting to preserve it a second time, whether in the same or 370 * a different session, should fail with EBUSY. 371 */ 372 TEST_F(liveupdate_device, prevent_double_preservation) 373 { 374 int session_fd1, session_fd2, mem_fd; 375 int ret; 376 377 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 378 if (self->fd1 < 0 && errno == ENOENT) 379 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 380 ASSERT_GE(self->fd1, 0); 381 382 session_fd1 = create_session(self->fd1, "double-preserve-session-1"); 383 ASSERT_GE(session_fd1, 0); 384 session_fd2 = create_session(self->fd1, "double-preserve-session-2"); 385 ASSERT_GE(session_fd2, 0); 386 387 mem_fd = memfd_create("test-memfd", 0); 388 ASSERT_GE(mem_fd, 0); 389 390 /* First preservation should succeed */ 391 ASSERT_EQ(preserve_fd(session_fd1, mem_fd, 0x1111), 0); 392 393 /* Second preservation in a different session should fail with EBUSY */ 394 ret = preserve_fd(session_fd2, mem_fd, 0x2222); 395 EXPECT_EQ(ret, -EBUSY); 396 397 /* Second preservation in the same session (different token) should fail with EBUSY */ 398 ret = preserve_fd(session_fd1, mem_fd, 0x3333); 399 EXPECT_EQ(ret, -EBUSY); 400 401 ASSERT_EQ(close(mem_fd), 0); 402 ASSERT_EQ(close(session_fd1), 0); 403 ASSERT_EQ(close(session_fd2), 0); 404 } 405 406 /* 407 * Test Case: Create Session with No Null Termination 408 * 409 * Verifies that filling the entire 64-byte name field with non-null characters 410 * (no '\0' terminator) is rejected by the kernel with EINVAL. 411 */ 412 TEST_F(liveupdate_device, create_session_no_null_termination) 413 { 414 struct liveupdate_ioctl_create_session args = {}; 415 416 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 417 if (self->fd1 < 0 && errno == ENOENT) 418 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 419 ASSERT_GE(self->fd1, 0); 420 421 /* Fill entire name field with 'X', no null terminator */ 422 args.size = sizeof(args); 423 memset(args.name, 'X', sizeof(args.name)); 424 425 EXPECT_LT(ioctl(self->fd1, LIVEUPDATE_IOCTL_CREATE_SESSION, &args), 0); 426 EXPECT_EQ(errno, EINVAL); 427 } 428 429 /* 430 * Test Case: Create Session with Empty Name 431 * 432 * Verifies that creating a session with an empty string name fails 433 * with EINVAL. 434 */ 435 TEST_F(liveupdate_device, create_session_empty_name) 436 { 437 int session_fd; 438 439 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 440 if (self->fd1 < 0 && errno == ENOENT) 441 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 442 ASSERT_GE(self->fd1, 0); 443 444 session_fd = create_session(self->fd1, ""); 445 EXPECT_EQ(session_fd, -EINVAL); 446 } 447 448 /* 449 * Test Case: Get Session Name 450 * 451 * Verifies that the full session name can be retrieved from a session file 452 * descriptor via ioctl. 453 */ 454 TEST_F(liveupdate_device, get_session_name) 455 { 456 char name_buf[LIVEUPDATE_SESSION_NAME_LENGTH] = {}; 457 const char *session_name = "get-name-test-session"; 458 int session_fd; 459 460 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 461 if (self->fd1 < 0 && errno == ENOENT) 462 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 463 ASSERT_GE(self->fd1, 0); 464 465 session_fd = create_session(self->fd1, session_name); 466 ASSERT_GE(session_fd, 0); 467 468 ASSERT_EQ(get_session_name(session_fd, name_buf, sizeof(name_buf)), 0); 469 ASSERT_STREQ(name_buf, session_name); 470 471 ASSERT_EQ(close(session_fd), 0); 472 } 473 474 /* 475 * Test Case: Get Session Name at Maximum Length 476 * 477 * Verifies that a session name using the full LIVEUPDATE_SESSION_NAME_LENGTH 478 * (minus the null terminator) can be correctly retrieved. 479 */ 480 TEST_F(liveupdate_device, get_session_name_max_length) 481 { 482 char name_buf[LIVEUPDATE_SESSION_NAME_LENGTH] = {}; 483 char long_name[LIVEUPDATE_SESSION_NAME_LENGTH]; 484 int session_fd; 485 486 memset(long_name, 'A', sizeof(long_name) - 1); 487 long_name[sizeof(long_name) - 1] = '\0'; 488 489 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 490 if (self->fd1 < 0 && errno == ENOENT) 491 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 492 ASSERT_GE(self->fd1, 0); 493 494 session_fd = create_session(self->fd1, long_name); 495 ASSERT_GE(session_fd, 0); 496 497 ASSERT_EQ(get_session_name(session_fd, name_buf, sizeof(name_buf)), 0); 498 ASSERT_STREQ(name_buf, long_name); 499 500 ASSERT_EQ(close(session_fd), 0); 501 } 502 503 /* 504 * Test Case: Manage Many Sessions 505 * 506 * Verifies that a large number of sessions can be created and then 507 * destroyed during normal system operation. This specifically tests the 508 * dynamic block allocation and reuse logic for session metadata management 509 * without preserving any files. 510 */ 511 TEST_F(liveupdate_device, preserve_many_sessions) 512 { 513 #define MANY_SESSIONS 2000 514 int session_fds[MANY_SESSIONS]; 515 int ret, i; 516 517 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 518 if (self->fd1 < 0 && errno == ENOENT) 519 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 520 ASSERT_GE(self->fd1, 0); 521 522 ret = luo_ensure_nofile_limit(MANY_SESSIONS); 523 if (ret == -EPERM) 524 SKIP(return, "Insufficient privileges to set RLIMIT_NOFILE"); 525 ASSERT_EQ(ret, 0); 526 527 for (i = 0; i < MANY_SESSIONS; i++) { 528 char name[64]; 529 530 snprintf(name, sizeof(name), "many-session-%d", i); 531 session_fds[i] = create_session(self->fd1, name); 532 ASSERT_GE(session_fds[i], 0); 533 } 534 535 for (i = 0; i < MANY_SESSIONS; i++) 536 ASSERT_EQ(close(session_fds[i]), 0); 537 } 538 539 /* 540 * Test Case: Preserve Many Files 541 * 542 * Verifies that a large number of files can be preserved in a single session 543 * and then destroyed during normal system operation. This tests the dynamic 544 * block allocation and management for outgoing files. 545 */ 546 TEST_F(liveupdate_device, preserve_many_files) 547 { 548 #define MANY_FILES 500 549 int mem_fds[MANY_FILES]; 550 int session_fd, ret, i; 551 552 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 553 if (self->fd1 < 0 && errno == ENOENT) 554 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 555 ASSERT_GE(self->fd1, 0); 556 557 session_fd = create_session(self->fd1, "many-files-test"); 558 ASSERT_GE(session_fd, 0); 559 560 ret = luo_ensure_nofile_limit(MANY_FILES + 10); 561 if (ret == -EPERM) 562 SKIP(return, "Insufficient privileges to set RLIMIT_NOFILE"); 563 ASSERT_EQ(ret, 0); 564 565 for (i = 0; i < MANY_FILES; i++) { 566 mem_fds[i] = memfd_create("test-memfd", 0); 567 ASSERT_GE(mem_fds[i], 0); 568 ASSERT_EQ(preserve_fd(session_fd, mem_fds[i], i), 0); 569 } 570 571 for (i = 0; i < MANY_FILES; i++) 572 ASSERT_EQ(close(mem_fds[i]), 0); 573 574 ASSERT_EQ(close(session_fd), 0); 575 } 576 577 TEST_HARNESS_MAIN 578