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 <libliveupdate.h> 28 #include <linux/liveupdate.h> 29 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 /* 93 * Test Case: Create Duplicate Session 94 * 95 * Verifies that attempting to create two sessions with the same name fails 96 * on the second attempt with EEXIST. 97 */ 98 TEST_F(liveupdate_device, create_duplicate_session) 99 { 100 int session_fd1, session_fd2; 101 102 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 103 if (self->fd1 < 0 && errno == ENOENT) 104 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 105 106 ASSERT_GE(self->fd1, 0); 107 108 session_fd1 = luo_create_session(self->fd1, "duplicate-session-test"); 109 ASSERT_GE(session_fd1, 0); 110 111 session_fd2 = luo_create_session(self->fd1, "duplicate-session-test"); 112 EXPECT_LT(session_fd2, 0); 113 EXPECT_EQ(-session_fd2, EEXIST); 114 115 ASSERT_EQ(close(session_fd1), 0); 116 } 117 118 /* 119 * Test Case: Create Distinct Sessions 120 * 121 * Verifies that creating two sessions with different names succeeds. 122 */ 123 TEST_F(liveupdate_device, create_distinct_sessions) 124 { 125 int session_fd1, session_fd2; 126 127 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 128 if (self->fd1 < 0 && errno == ENOENT) 129 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 130 131 ASSERT_GE(self->fd1, 0); 132 133 session_fd1 = luo_create_session(self->fd1, "distinct-session-1"); 134 ASSERT_GE(session_fd1, 0); 135 136 session_fd2 = luo_create_session(self->fd1, "distinct-session-2"); 137 ASSERT_GE(session_fd2, 0); 138 139 ASSERT_EQ(close(session_fd1), 0); 140 ASSERT_EQ(close(session_fd2), 0); 141 } 142 143 /* 144 * Test Case: Preserve MemFD 145 * 146 * Verifies that a valid memfd can be successfully preserved in a session and 147 * that its contents remain intact after the preservation call. 148 */ 149 TEST_F(liveupdate_device, preserve_memfd) 150 { 151 const char *test_str = "hello liveupdate"; 152 char read_buf[64] = {}; 153 int session_fd, mem_fd; 154 155 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 156 if (self->fd1 < 0 && errno == ENOENT) 157 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 158 ASSERT_GE(self->fd1, 0); 159 160 session_fd = luo_create_session(self->fd1, "preserve-memfd-test"); 161 ASSERT_GE(session_fd, 0); 162 163 mem_fd = memfd_create("test-memfd", 0); 164 ASSERT_GE(mem_fd, 0); 165 166 ASSERT_EQ(write(mem_fd, test_str, strlen(test_str)), strlen(test_str)); 167 ASSERT_EQ(luo_session_preserve_fd(session_fd, mem_fd, 0x1234), 0); 168 ASSERT_EQ(close(session_fd), 0); 169 170 ASSERT_EQ(lseek(mem_fd, 0, SEEK_SET), 0); 171 ASSERT_EQ(read(mem_fd, read_buf, sizeof(read_buf)), strlen(test_str)); 172 ASSERT_STREQ(read_buf, test_str); 173 ASSERT_EQ(close(mem_fd), 0); 174 } 175 176 /* 177 * Test Case: Preserve Multiple MemFDs 178 * 179 * Verifies that multiple memfds can be preserved in a single session, 180 * each with a unique token, and that their contents remain distinct and 181 * correct after preservation. 182 */ 183 TEST_F(liveupdate_device, preserve_multiple_memfds) 184 { 185 const char *test_str1 = "data for memfd one"; 186 const char *test_str2 = "data for memfd two"; 187 char read_buf[64] = {}; 188 int session_fd, mem_fd1, mem_fd2; 189 190 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 191 if (self->fd1 < 0 && errno == ENOENT) 192 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 193 ASSERT_GE(self->fd1, 0); 194 195 session_fd = luo_create_session(self->fd1, "preserve-multi-memfd-test"); 196 ASSERT_GE(session_fd, 0); 197 198 mem_fd1 = memfd_create("test-memfd-1", 0); 199 ASSERT_GE(mem_fd1, 0); 200 mem_fd2 = memfd_create("test-memfd-2", 0); 201 ASSERT_GE(mem_fd2, 0); 202 203 ASSERT_EQ(write(mem_fd1, test_str1, strlen(test_str1)), strlen(test_str1)); 204 ASSERT_EQ(write(mem_fd2, test_str2, strlen(test_str2)), strlen(test_str2)); 205 206 ASSERT_EQ(luo_session_preserve_fd(session_fd, mem_fd1, 0xAAAA), 0); 207 ASSERT_EQ(luo_session_preserve_fd(session_fd, mem_fd2, 0xBBBB), 0); 208 209 memset(read_buf, 0, sizeof(read_buf)); 210 ASSERT_EQ(lseek(mem_fd1, 0, SEEK_SET), 0); 211 ASSERT_EQ(read(mem_fd1, read_buf, sizeof(read_buf)), strlen(test_str1)); 212 ASSERT_STREQ(read_buf, test_str1); 213 214 memset(read_buf, 0, sizeof(read_buf)); 215 ASSERT_EQ(lseek(mem_fd2, 0, SEEK_SET), 0); 216 ASSERT_EQ(read(mem_fd2, read_buf, sizeof(read_buf)), strlen(test_str2)); 217 ASSERT_STREQ(read_buf, test_str2); 218 219 ASSERT_EQ(close(mem_fd1), 0); 220 ASSERT_EQ(close(mem_fd2), 0); 221 ASSERT_EQ(close(session_fd), 0); 222 } 223 224 /* 225 * Test Case: Preserve Complex Scenario 226 * 227 * Verifies a more complex scenario with multiple sessions and a mix of empty 228 * and non-empty memfds distributed across them. 229 */ 230 TEST_F(liveupdate_device, preserve_complex_scenario) 231 { 232 const char *data1 = "data for session 1"; 233 const char *data2 = "data for session 2"; 234 char read_buf[64] = {}; 235 int session_fd1, session_fd2; 236 int mem_fd_data1, mem_fd_empty1, mem_fd_data2, mem_fd_empty2; 237 238 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 239 if (self->fd1 < 0 && errno == ENOENT) 240 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 241 ASSERT_GE(self->fd1, 0); 242 243 session_fd1 = luo_create_session(self->fd1, "complex-session-1"); 244 ASSERT_GE(session_fd1, 0); 245 session_fd2 = luo_create_session(self->fd1, "complex-session-2"); 246 ASSERT_GE(session_fd2, 0); 247 248 mem_fd_data1 = memfd_create("data1", 0); 249 ASSERT_GE(mem_fd_data1, 0); 250 ASSERT_EQ(write(mem_fd_data1, data1, strlen(data1)), strlen(data1)); 251 252 mem_fd_empty1 = memfd_create("empty1", 0); 253 ASSERT_GE(mem_fd_empty1, 0); 254 255 mem_fd_data2 = memfd_create("data2", 0); 256 ASSERT_GE(mem_fd_data2, 0); 257 ASSERT_EQ(write(mem_fd_data2, data2, strlen(data2)), strlen(data2)); 258 259 mem_fd_empty2 = memfd_create("empty2", 0); 260 ASSERT_GE(mem_fd_empty2, 0); 261 262 ASSERT_EQ(luo_session_preserve_fd(session_fd1, mem_fd_data1, 0x1111), 0); 263 ASSERT_EQ(luo_session_preserve_fd(session_fd1, mem_fd_empty1, 0x2222), 0); 264 ASSERT_EQ(luo_session_preserve_fd(session_fd2, mem_fd_data2, 0x3333), 0); 265 ASSERT_EQ(luo_session_preserve_fd(session_fd2, mem_fd_empty2, 0x4444), 0); 266 267 ASSERT_EQ(lseek(mem_fd_data1, 0, SEEK_SET), 0); 268 ASSERT_EQ(read(mem_fd_data1, read_buf, sizeof(read_buf)), strlen(data1)); 269 ASSERT_STREQ(read_buf, data1); 270 271 memset(read_buf, 0, sizeof(read_buf)); 272 ASSERT_EQ(lseek(mem_fd_data2, 0, SEEK_SET), 0); 273 ASSERT_EQ(read(mem_fd_data2, read_buf, sizeof(read_buf)), strlen(data2)); 274 ASSERT_STREQ(read_buf, data2); 275 276 ASSERT_EQ(lseek(mem_fd_empty1, 0, SEEK_SET), 0); 277 ASSERT_EQ(read(mem_fd_empty1, read_buf, sizeof(read_buf)), 0); 278 279 ASSERT_EQ(lseek(mem_fd_empty2, 0, SEEK_SET), 0); 280 ASSERT_EQ(read(mem_fd_empty2, read_buf, sizeof(read_buf)), 0); 281 282 ASSERT_EQ(close(mem_fd_data1), 0); 283 ASSERT_EQ(close(mem_fd_empty1), 0); 284 ASSERT_EQ(close(mem_fd_data2), 0); 285 ASSERT_EQ(close(mem_fd_empty2), 0); 286 ASSERT_EQ(close(session_fd1), 0); 287 ASSERT_EQ(close(session_fd2), 0); 288 } 289 290 /* 291 * Test Case: Preserve Unsupported File Descriptor 292 * 293 * Verifies that attempting to preserve a file descriptor that does not have 294 * a registered Live Update handler fails gracefully. 295 * Uses /dev/null as a representative of a file type (character device) 296 * that is not supported by the orchestrator. 297 */ 298 TEST_F(liveupdate_device, preserve_unsupported_fd) 299 { 300 int session_fd, unsupported_fd; 301 int ret; 302 303 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 304 if (self->fd1 < 0 && errno == ENOENT) 305 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 306 ASSERT_GE(self->fd1, 0); 307 308 session_fd = luo_create_session(self->fd1, "unsupported-fd-test"); 309 ASSERT_GE(session_fd, 0); 310 311 unsupported_fd = open("/dev/null", O_RDWR); 312 ASSERT_GE(unsupported_fd, 0); 313 314 ret = luo_session_preserve_fd(session_fd, unsupported_fd, 0xDEAD); 315 EXPECT_EQ(ret, -ENOENT); 316 317 ASSERT_EQ(close(unsupported_fd), 0); 318 ASSERT_EQ(close(session_fd), 0); 319 } 320 321 /* 322 * Test Case: Prevent Double Preservation 323 * 324 * Verifies that a file (memfd) can only be preserved once across all active 325 * sessions. Attempting to preserve it a second time, whether in the same or 326 * a different session, should fail with EBUSY. 327 */ 328 TEST_F(liveupdate_device, prevent_double_preservation) 329 { 330 int session_fd1, session_fd2, mem_fd; 331 int ret; 332 333 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 334 if (self->fd1 < 0 && errno == ENOENT) 335 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 336 ASSERT_GE(self->fd1, 0); 337 338 session_fd1 = luo_create_session(self->fd1, "double-preserve-session-1"); 339 ASSERT_GE(session_fd1, 0); 340 session_fd2 = luo_create_session(self->fd1, "double-preserve-session-2"); 341 ASSERT_GE(session_fd2, 0); 342 343 mem_fd = memfd_create("test-memfd", 0); 344 ASSERT_GE(mem_fd, 0); 345 346 /* First preservation should succeed */ 347 ASSERT_EQ(luo_session_preserve_fd(session_fd1, mem_fd, 0x1111), 0); 348 349 /* Second preservation in a different session should fail with EBUSY */ 350 ret = luo_session_preserve_fd(session_fd2, mem_fd, 0x2222); 351 EXPECT_EQ(ret, -EBUSY); 352 353 /* Second preservation in the same session (different token) should fail with EBUSY */ 354 ret = luo_session_preserve_fd(session_fd1, mem_fd, 0x3333); 355 EXPECT_EQ(ret, -EBUSY); 356 357 ASSERT_EQ(close(mem_fd), 0); 358 ASSERT_EQ(close(session_fd1), 0); 359 ASSERT_EQ(close(session_fd2), 0); 360 } 361 362 /* 363 * Test Case: Create Session with No Null Termination 364 * 365 * Verifies that filling the entire 64-byte name field with non-null characters 366 * (no '\0' terminator) is rejected by the kernel with EINVAL. 367 */ 368 TEST_F(liveupdate_device, create_session_no_null_termination) 369 { 370 struct liveupdate_ioctl_create_session args = {}; 371 372 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 373 if (self->fd1 < 0 && errno == ENOENT) 374 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 375 ASSERT_GE(self->fd1, 0); 376 377 /* Fill entire name field with 'X', no null terminator */ 378 args.size = sizeof(args); 379 memset(args.name, 'X', sizeof(args.name)); 380 381 EXPECT_LT(ioctl(self->fd1, LIVEUPDATE_IOCTL_CREATE_SESSION, &args), 0); 382 EXPECT_EQ(errno, EINVAL); 383 } 384 385 /* 386 * Test Case: Create Session with Empty Name 387 * 388 * Verifies that creating a session with an empty string name fails 389 * with EINVAL. 390 */ 391 TEST_F(liveupdate_device, create_session_empty_name) 392 { 393 int session_fd; 394 395 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 396 if (self->fd1 < 0 && errno == ENOENT) 397 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 398 ASSERT_GE(self->fd1, 0); 399 400 session_fd = luo_create_session(self->fd1, ""); 401 EXPECT_EQ(session_fd, -EINVAL); 402 } 403 404 /* 405 * Test Case: Get Session Name 406 * 407 * Verifies that the full session name can be retrieved from a session file 408 * descriptor via ioctl. 409 */ 410 TEST_F(liveupdate_device, get_session_name) 411 { 412 char name_buf[LIVEUPDATE_SESSION_NAME_LENGTH] = {}; 413 const char *session_name = "get-name-test-session"; 414 int session_fd; 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 session_fd = luo_create_session(self->fd1, session_name); 422 ASSERT_GE(session_fd, 0); 423 424 ASSERT_EQ(luo_get_session_name(session_fd, name_buf, sizeof(name_buf)), 0); 425 ASSERT_STREQ(name_buf, session_name); 426 427 ASSERT_EQ(close(session_fd), 0); 428 } 429 430 /* 431 * Test Case: Get Session Name at Maximum Length 432 * 433 * Verifies that a session name using the full LIVEUPDATE_SESSION_NAME_LENGTH 434 * (minus the null terminator) can be correctly retrieved. 435 */ 436 TEST_F(liveupdate_device, get_session_name_max_length) 437 { 438 char name_buf[LIVEUPDATE_SESSION_NAME_LENGTH] = {}; 439 char long_name[LIVEUPDATE_SESSION_NAME_LENGTH]; 440 int session_fd; 441 442 memset(long_name, 'A', sizeof(long_name) - 1); 443 long_name[sizeof(long_name) - 1] = '\0'; 444 445 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 446 if (self->fd1 < 0 && errno == ENOENT) 447 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 448 ASSERT_GE(self->fd1, 0); 449 450 session_fd = luo_create_session(self->fd1, long_name); 451 ASSERT_GE(session_fd, 0); 452 453 ASSERT_EQ(luo_get_session_name(session_fd, name_buf, sizeof(name_buf)), 0); 454 ASSERT_STREQ(name_buf, long_name); 455 456 ASSERT_EQ(close(session_fd), 0); 457 } 458 459 /* 460 * Test Case: Manage Many Sessions 461 * 462 * Verifies that a large number of sessions can be created and then 463 * destroyed during normal system operation. This specifically tests the 464 * dynamic block allocation and reuse logic for session metadata management 465 * without preserving any files. 466 */ 467 TEST_F(liveupdate_device, preserve_many_sessions) 468 { 469 #define MANY_SESSIONS 2000 470 int session_fds[MANY_SESSIONS]; 471 int ret, i; 472 473 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 474 if (self->fd1 < 0 && errno == ENOENT) 475 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 476 ASSERT_GE(self->fd1, 0); 477 478 ret = luo_ensure_nofile_limit(MANY_SESSIONS); 479 if (ret == -EPERM) 480 SKIP(return, "Insufficient privileges to set RLIMIT_NOFILE"); 481 ASSERT_EQ(ret, 0); 482 483 for (i = 0; i < MANY_SESSIONS; i++) { 484 char name[64]; 485 486 snprintf(name, sizeof(name), "many-session-%d", i); 487 session_fds[i] = luo_create_session(self->fd1, name); 488 ASSERT_GE(session_fds[i], 0); 489 } 490 491 for (i = 0; i < MANY_SESSIONS; i++) 492 ASSERT_EQ(close(session_fds[i]), 0); 493 } 494 495 /* 496 * Test Case: Preserve Many Files 497 * 498 * Verifies that a large number of files can be preserved in a single session 499 * and then destroyed during normal system operation. This tests the dynamic 500 * block allocation and management for outgoing files. 501 */ 502 TEST_F(liveupdate_device, preserve_many_files) 503 { 504 #define MANY_FILES 500 505 int mem_fds[MANY_FILES]; 506 int session_fd, ret, i; 507 508 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 509 if (self->fd1 < 0 && errno == ENOENT) 510 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 511 ASSERT_GE(self->fd1, 0); 512 513 session_fd = luo_create_session(self->fd1, "many-files-test"); 514 ASSERT_GE(session_fd, 0); 515 516 ret = luo_ensure_nofile_limit(MANY_FILES + 10); 517 if (ret == -EPERM) 518 SKIP(return, "Insufficient privileges to set RLIMIT_NOFILE"); 519 ASSERT_EQ(ret, 0); 520 521 for (i = 0; i < MANY_FILES; i++) { 522 mem_fds[i] = memfd_create("test-memfd", 0); 523 ASSERT_GE(mem_fds[i], 0); 524 ASSERT_EQ(luo_session_preserve_fd(session_fd, mem_fds[i], i), 0); 525 } 526 527 for (i = 0; i < MANY_FILES; i++) 528 ASSERT_EQ(close(mem_fds[i]), 0); 529 530 ASSERT_EQ(close(session_fd), 0); 531 } 532 533 TEST_HARNESS_MAIN 534