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 "../kselftest.h" 30 #include "../kselftest_harness.h" 31 32 #define LIVEUPDATE_DEV "/dev/liveupdate" 33 34 FIXTURE(liveupdate_device) { 35 int fd1; 36 int fd2; 37 }; 38 39 FIXTURE_SETUP(liveupdate_device) 40 { 41 self->fd1 = -1; 42 self->fd2 = -1; 43 } 44 45 FIXTURE_TEARDOWN(liveupdate_device) 46 { 47 if (self->fd1 >= 0) 48 close(self->fd1); 49 if (self->fd2 >= 0) 50 close(self->fd2); 51 } 52 53 /* 54 * Test Case: Basic Open and Close 55 * 56 * Verifies that the /dev/liveupdate device can be opened and subsequently 57 * closed without errors. Skips if the device does not exist. 58 */ 59 TEST_F(liveupdate_device, basic_open_close) 60 { 61 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 62 63 if (self->fd1 < 0 && errno == ENOENT) 64 SKIP(return, "%s does not exist.", LIVEUPDATE_DEV); 65 66 ASSERT_GE(self->fd1, 0); 67 ASSERT_EQ(close(self->fd1), 0); 68 self->fd1 = -1; 69 } 70 71 /* 72 * Test Case: Exclusive Open Enforcement 73 * 74 * Verifies that the /dev/liveupdate device can only be opened by one process 75 * at a time. It checks that a second attempt to open the device fails with 76 * the EBUSY error code. 77 */ 78 TEST_F(liveupdate_device, exclusive_open) 79 { 80 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 81 82 if (self->fd1 < 0 && errno == ENOENT) 83 SKIP(return, "%s does not exist.", LIVEUPDATE_DEV); 84 85 ASSERT_GE(self->fd1, 0); 86 self->fd2 = open(LIVEUPDATE_DEV, O_RDWR); 87 EXPECT_LT(self->fd2, 0); 88 EXPECT_EQ(errno, EBUSY); 89 } 90 91 /* Helper function to create a LUO session via ioctl. */ 92 static int create_session(int lu_fd, const char *name) 93 { 94 struct liveupdate_ioctl_create_session args = {}; 95 96 args.size = sizeof(args); 97 strncpy((char *)args.name, name, sizeof(args.name) - 1); 98 99 if (ioctl(lu_fd, LIVEUPDATE_IOCTL_CREATE_SESSION, &args)) 100 return -errno; 101 102 return args.fd; 103 } 104 105 /* 106 * Test Case: Create Duplicate Session 107 * 108 * Verifies that attempting to create two sessions with the same name fails 109 * on the second attempt with EEXIST. 110 */ 111 TEST_F(liveupdate_device, create_duplicate_session) 112 { 113 int session_fd1, session_fd2; 114 115 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 116 if (self->fd1 < 0 && errno == ENOENT) 117 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 118 119 ASSERT_GE(self->fd1, 0); 120 121 session_fd1 = create_session(self->fd1, "duplicate-session-test"); 122 ASSERT_GE(session_fd1, 0); 123 124 session_fd2 = create_session(self->fd1, "duplicate-session-test"); 125 EXPECT_LT(session_fd2, 0); 126 EXPECT_EQ(-session_fd2, EEXIST); 127 128 ASSERT_EQ(close(session_fd1), 0); 129 } 130 131 /* 132 * Test Case: Create Distinct Sessions 133 * 134 * Verifies that creating two sessions with different names succeeds. 135 */ 136 TEST_F(liveupdate_device, create_distinct_sessions) 137 { 138 int session_fd1, session_fd2; 139 140 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 141 if (self->fd1 < 0 && errno == ENOENT) 142 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 143 144 ASSERT_GE(self->fd1, 0); 145 146 session_fd1 = create_session(self->fd1, "distinct-session-1"); 147 ASSERT_GE(session_fd1, 0); 148 149 session_fd2 = create_session(self->fd1, "distinct-session-2"); 150 ASSERT_GE(session_fd2, 0); 151 152 ASSERT_EQ(close(session_fd1), 0); 153 ASSERT_EQ(close(session_fd2), 0); 154 } 155 156 static int preserve_fd(int session_fd, int fd_to_preserve, __u64 token) 157 { 158 struct liveupdate_session_preserve_fd args = {}; 159 160 args.size = sizeof(args); 161 args.fd = fd_to_preserve; 162 args.token = token; 163 164 if (ioctl(session_fd, LIVEUPDATE_SESSION_PRESERVE_FD, &args)) 165 return -errno; 166 167 return 0; 168 } 169 170 /* 171 * Test Case: Preserve MemFD 172 * 173 * Verifies that a valid memfd can be successfully preserved in a session and 174 * that its contents remain intact after the preservation call. 175 */ 176 TEST_F(liveupdate_device, preserve_memfd) 177 { 178 const char *test_str = "hello liveupdate"; 179 char read_buf[64] = {}; 180 int session_fd, mem_fd; 181 182 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 183 if (self->fd1 < 0 && errno == ENOENT) 184 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 185 ASSERT_GE(self->fd1, 0); 186 187 session_fd = create_session(self->fd1, "preserve-memfd-test"); 188 ASSERT_GE(session_fd, 0); 189 190 mem_fd = memfd_create("test-memfd", 0); 191 ASSERT_GE(mem_fd, 0); 192 193 ASSERT_EQ(write(mem_fd, test_str, strlen(test_str)), strlen(test_str)); 194 ASSERT_EQ(preserve_fd(session_fd, mem_fd, 0x1234), 0); 195 ASSERT_EQ(close(session_fd), 0); 196 197 ASSERT_EQ(lseek(mem_fd, 0, SEEK_SET), 0); 198 ASSERT_EQ(read(mem_fd, read_buf, sizeof(read_buf)), strlen(test_str)); 199 ASSERT_STREQ(read_buf, test_str); 200 ASSERT_EQ(close(mem_fd), 0); 201 } 202 203 /* 204 * Test Case: Preserve Multiple MemFDs 205 * 206 * Verifies that multiple memfds can be preserved in a single session, 207 * each with a unique token, and that their contents remain distinct and 208 * correct after preservation. 209 */ 210 TEST_F(liveupdate_device, preserve_multiple_memfds) 211 { 212 const char *test_str1 = "data for memfd one"; 213 const char *test_str2 = "data for memfd two"; 214 char read_buf[64] = {}; 215 int session_fd, mem_fd1, mem_fd2; 216 217 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 218 if (self->fd1 < 0 && errno == ENOENT) 219 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 220 ASSERT_GE(self->fd1, 0); 221 222 session_fd = create_session(self->fd1, "preserve-multi-memfd-test"); 223 ASSERT_GE(session_fd, 0); 224 225 mem_fd1 = memfd_create("test-memfd-1", 0); 226 ASSERT_GE(mem_fd1, 0); 227 mem_fd2 = memfd_create("test-memfd-2", 0); 228 ASSERT_GE(mem_fd2, 0); 229 230 ASSERT_EQ(write(mem_fd1, test_str1, strlen(test_str1)), strlen(test_str1)); 231 ASSERT_EQ(write(mem_fd2, test_str2, strlen(test_str2)), strlen(test_str2)); 232 233 ASSERT_EQ(preserve_fd(session_fd, mem_fd1, 0xAAAA), 0); 234 ASSERT_EQ(preserve_fd(session_fd, mem_fd2, 0xBBBB), 0); 235 236 memset(read_buf, 0, sizeof(read_buf)); 237 ASSERT_EQ(lseek(mem_fd1, 0, SEEK_SET), 0); 238 ASSERT_EQ(read(mem_fd1, read_buf, sizeof(read_buf)), strlen(test_str1)); 239 ASSERT_STREQ(read_buf, test_str1); 240 241 memset(read_buf, 0, sizeof(read_buf)); 242 ASSERT_EQ(lseek(mem_fd2, 0, SEEK_SET), 0); 243 ASSERT_EQ(read(mem_fd2, read_buf, sizeof(read_buf)), strlen(test_str2)); 244 ASSERT_STREQ(read_buf, test_str2); 245 246 ASSERT_EQ(close(mem_fd1), 0); 247 ASSERT_EQ(close(mem_fd2), 0); 248 ASSERT_EQ(close(session_fd), 0); 249 } 250 251 /* 252 * Test Case: Preserve Complex Scenario 253 * 254 * Verifies a more complex scenario with multiple sessions and a mix of empty 255 * and non-empty memfds distributed across them. 256 */ 257 TEST_F(liveupdate_device, preserve_complex_scenario) 258 { 259 const char *data1 = "data for session 1"; 260 const char *data2 = "data for session 2"; 261 char read_buf[64] = {}; 262 int session_fd1, session_fd2; 263 int mem_fd_data1, mem_fd_empty1, mem_fd_data2, mem_fd_empty2; 264 265 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 266 if (self->fd1 < 0 && errno == ENOENT) 267 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 268 ASSERT_GE(self->fd1, 0); 269 270 session_fd1 = create_session(self->fd1, "complex-session-1"); 271 ASSERT_GE(session_fd1, 0); 272 session_fd2 = create_session(self->fd1, "complex-session-2"); 273 ASSERT_GE(session_fd2, 0); 274 275 mem_fd_data1 = memfd_create("data1", 0); 276 ASSERT_GE(mem_fd_data1, 0); 277 ASSERT_EQ(write(mem_fd_data1, data1, strlen(data1)), strlen(data1)); 278 279 mem_fd_empty1 = memfd_create("empty1", 0); 280 ASSERT_GE(mem_fd_empty1, 0); 281 282 mem_fd_data2 = memfd_create("data2", 0); 283 ASSERT_GE(mem_fd_data2, 0); 284 ASSERT_EQ(write(mem_fd_data2, data2, strlen(data2)), strlen(data2)); 285 286 mem_fd_empty2 = memfd_create("empty2", 0); 287 ASSERT_GE(mem_fd_empty2, 0); 288 289 ASSERT_EQ(preserve_fd(session_fd1, mem_fd_data1, 0x1111), 0); 290 ASSERT_EQ(preserve_fd(session_fd1, mem_fd_empty1, 0x2222), 0); 291 ASSERT_EQ(preserve_fd(session_fd2, mem_fd_data2, 0x3333), 0); 292 ASSERT_EQ(preserve_fd(session_fd2, mem_fd_empty2, 0x4444), 0); 293 294 ASSERT_EQ(lseek(mem_fd_data1, 0, SEEK_SET), 0); 295 ASSERT_EQ(read(mem_fd_data1, read_buf, sizeof(read_buf)), strlen(data1)); 296 ASSERT_STREQ(read_buf, data1); 297 298 memset(read_buf, 0, sizeof(read_buf)); 299 ASSERT_EQ(lseek(mem_fd_data2, 0, SEEK_SET), 0); 300 ASSERT_EQ(read(mem_fd_data2, read_buf, sizeof(read_buf)), strlen(data2)); 301 ASSERT_STREQ(read_buf, data2); 302 303 ASSERT_EQ(lseek(mem_fd_empty1, 0, SEEK_SET), 0); 304 ASSERT_EQ(read(mem_fd_empty1, read_buf, sizeof(read_buf)), 0); 305 306 ASSERT_EQ(lseek(mem_fd_empty2, 0, SEEK_SET), 0); 307 ASSERT_EQ(read(mem_fd_empty2, read_buf, sizeof(read_buf)), 0); 308 309 ASSERT_EQ(close(mem_fd_data1), 0); 310 ASSERT_EQ(close(mem_fd_empty1), 0); 311 ASSERT_EQ(close(mem_fd_data2), 0); 312 ASSERT_EQ(close(mem_fd_empty2), 0); 313 ASSERT_EQ(close(session_fd1), 0); 314 ASSERT_EQ(close(session_fd2), 0); 315 } 316 317 /* 318 * Test Case: Preserve Unsupported File Descriptor 319 * 320 * Verifies that attempting to preserve a file descriptor that does not have 321 * a registered Live Update handler fails gracefully. 322 * Uses /dev/null as a representative of a file type (character device) 323 * that is not supported by the orchestrator. 324 */ 325 TEST_F(liveupdate_device, preserve_unsupported_fd) 326 { 327 int session_fd, unsupported_fd; 328 int ret; 329 330 self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); 331 if (self->fd1 < 0 && errno == ENOENT) 332 SKIP(return, "%s does not exist", LIVEUPDATE_DEV); 333 ASSERT_GE(self->fd1, 0); 334 335 session_fd = create_session(self->fd1, "unsupported-fd-test"); 336 ASSERT_GE(session_fd, 0); 337 338 unsupported_fd = open("/dev/null", O_RDWR); 339 ASSERT_GE(unsupported_fd, 0); 340 341 ret = preserve_fd(session_fd, unsupported_fd, 0xDEAD); 342 EXPECT_EQ(ret, -ENOENT); 343 344 ASSERT_EQ(close(unsupported_fd), 0); 345 ASSERT_EQ(close(session_fd), 0); 346 } 347 348 TEST_HARNESS_MAIN 349