File Index
Files and directories
Sign in
FILE

liveupdate.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/liveupdate/liveupdate.c C
  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
 34FIXTURE(liveupdate_device) {
 35	int fd1;
 36	int fd2;
 37};
 38
 39FIXTURE_SETUP(liveupdate_device)
 40{
 41	self->fd1 = -1;
 42	self->fd2 = -1;
 43}
 44
 45FIXTURE_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 */
 59TEST_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 */
 78TEST_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. */
 92static 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 */
111TEST_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 */
136TEST_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
156static 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 */
176TEST_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 */
210TEST_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 */
257TEST_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 */
325TEST_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
348TEST_HARNESS_MAIN
~/ Read-only file preview.