xref: /linux/tools/testing/selftests/liveupdate/liveupdate.c (revision 5ba3f30643cbdd79fb82e525aa1ca55b62fcc7ac)
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