162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Author: Aleksa Sarai <cyphar@cyphar.com> 462306a36Sopenharmony_ci * Copyright (C) 2018-2019 SUSE LLC. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define _GNU_SOURCE 862306a36Sopenharmony_ci#include <fcntl.h> 962306a36Sopenharmony_ci#include <sched.h> 1062306a36Sopenharmony_ci#include <sys/stat.h> 1162306a36Sopenharmony_ci#include <sys/types.h> 1262306a36Sopenharmony_ci#include <sys/mount.h> 1362306a36Sopenharmony_ci#include <stdlib.h> 1462306a36Sopenharmony_ci#include <stdbool.h> 1562306a36Sopenharmony_ci#include <string.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "../kselftest.h" 1862306a36Sopenharmony_ci#include "helpers.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * O_LARGEFILE is set to 0 by glibc. 2262306a36Sopenharmony_ci * XXX: This is wrong on {mips, parisc, powerpc, sparc}. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci#undef O_LARGEFILE 2562306a36Sopenharmony_ci#ifdef __aarch64__ 2662306a36Sopenharmony_ci#define O_LARGEFILE 0x20000 2762306a36Sopenharmony_ci#else 2862306a36Sopenharmony_ci#define O_LARGEFILE 0x8000 2962306a36Sopenharmony_ci#endif 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct open_how_ext { 3262306a36Sopenharmony_ci struct open_how inner; 3362306a36Sopenharmony_ci uint32_t extra1; 3462306a36Sopenharmony_ci char pad1[128]; 3562306a36Sopenharmony_ci uint32_t extra2; 3662306a36Sopenharmony_ci char pad2[128]; 3762306a36Sopenharmony_ci uint32_t extra3; 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct struct_test { 4162306a36Sopenharmony_ci const char *name; 4262306a36Sopenharmony_ci struct open_how_ext arg; 4362306a36Sopenharmony_ci size_t size; 4462306a36Sopenharmony_ci int err; 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define NUM_OPENAT2_STRUCT_TESTS 7 4862306a36Sopenharmony_ci#define NUM_OPENAT2_STRUCT_VARIATIONS 13 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_civoid test_openat2_struct(void) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci int misalignments[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 17, 87 }; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci struct struct_test tests[] = { 5562306a36Sopenharmony_ci /* Normal struct. */ 5662306a36Sopenharmony_ci { .name = "normal struct", 5762306a36Sopenharmony_ci .arg.inner.flags = O_RDONLY, 5862306a36Sopenharmony_ci .size = sizeof(struct open_how) }, 5962306a36Sopenharmony_ci /* Bigger struct, with zeroed out end. */ 6062306a36Sopenharmony_ci { .name = "bigger struct (zeroed out)", 6162306a36Sopenharmony_ci .arg.inner.flags = O_RDONLY, 6262306a36Sopenharmony_ci .size = sizeof(struct open_how_ext) }, 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* TODO: Once expanded, check zero-padding. */ 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* Smaller than version-0 struct. */ 6762306a36Sopenharmony_ci { .name = "zero-sized 'struct'", 6862306a36Sopenharmony_ci .arg.inner.flags = O_RDONLY, .size = 0, .err = -EINVAL }, 6962306a36Sopenharmony_ci { .name = "smaller-than-v0 struct", 7062306a36Sopenharmony_ci .arg.inner.flags = O_RDONLY, 7162306a36Sopenharmony_ci .size = OPEN_HOW_SIZE_VER0 - 1, .err = -EINVAL }, 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* Bigger struct, with non-zero trailing bytes. */ 7462306a36Sopenharmony_ci { .name = "bigger struct (non-zero data in first 'future field')", 7562306a36Sopenharmony_ci .arg.inner.flags = O_RDONLY, .arg.extra1 = 0xdeadbeef, 7662306a36Sopenharmony_ci .size = sizeof(struct open_how_ext), .err = -E2BIG }, 7762306a36Sopenharmony_ci { .name = "bigger struct (non-zero data in middle of 'future fields')", 7862306a36Sopenharmony_ci .arg.inner.flags = O_RDONLY, .arg.extra2 = 0xfeedcafe, 7962306a36Sopenharmony_ci .size = sizeof(struct open_how_ext), .err = -E2BIG }, 8062306a36Sopenharmony_ci { .name = "bigger struct (non-zero data at end of 'future fields')", 8162306a36Sopenharmony_ci .arg.inner.flags = O_RDONLY, .arg.extra3 = 0xabad1dea, 8262306a36Sopenharmony_ci .size = sizeof(struct open_how_ext), .err = -E2BIG }, 8362306a36Sopenharmony_ci }; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_LEN(misalignments) != NUM_OPENAT2_STRUCT_VARIATIONS); 8662306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_STRUCT_TESTS); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci for (int i = 0; i < ARRAY_LEN(tests); i++) { 8962306a36Sopenharmony_ci struct struct_test *test = &tests[i]; 9062306a36Sopenharmony_ci struct open_how_ext how_ext = test->arg; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci for (int j = 0; j < ARRAY_LEN(misalignments); j++) { 9362306a36Sopenharmony_ci int fd, misalign = misalignments[j]; 9462306a36Sopenharmony_ci char *fdpath = NULL; 9562306a36Sopenharmony_ci bool failed; 9662306a36Sopenharmony_ci void (*resultfn)(const char *msg, ...) = ksft_test_result_pass; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci void *copy = NULL, *how_copy = &how_ext; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (!openat2_supported) { 10162306a36Sopenharmony_ci ksft_print_msg("openat2(2) unsupported\n"); 10262306a36Sopenharmony_ci resultfn = ksft_test_result_skip; 10362306a36Sopenharmony_ci goto skip; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (misalign) { 10762306a36Sopenharmony_ci /* 10862306a36Sopenharmony_ci * Explicitly misalign the structure copying it with the given 10962306a36Sopenharmony_ci * (mis)alignment offset. The other data is set to be non-zero to 11062306a36Sopenharmony_ci * make sure that non-zero bytes outside the struct aren't checked 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * This is effectively to check that is_zeroed_user() works. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci copy = malloc(misalign + sizeof(how_ext)); 11562306a36Sopenharmony_ci how_copy = copy + misalign; 11662306a36Sopenharmony_ci memset(copy, 0xff, misalign); 11762306a36Sopenharmony_ci memcpy(how_copy, &how_ext, sizeof(how_ext)); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci fd = raw_openat2(AT_FDCWD, ".", how_copy, test->size); 12162306a36Sopenharmony_ci if (test->err >= 0) 12262306a36Sopenharmony_ci failed = (fd < 0); 12362306a36Sopenharmony_ci else 12462306a36Sopenharmony_ci failed = (fd != test->err); 12562306a36Sopenharmony_ci if (fd >= 0) { 12662306a36Sopenharmony_ci fdpath = fdreadlink(fd); 12762306a36Sopenharmony_ci close(fd); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (failed) { 13162306a36Sopenharmony_ci resultfn = ksft_test_result_fail; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci ksft_print_msg("openat2 unexpectedly returned "); 13462306a36Sopenharmony_ci if (fdpath) 13562306a36Sopenharmony_ci ksft_print_msg("%d['%s']\n", fd, fdpath); 13662306a36Sopenharmony_ci else 13762306a36Sopenharmony_ci ksft_print_msg("%d (%s)\n", fd, strerror(-fd)); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ciskip: 14162306a36Sopenharmony_ci if (test->err >= 0) 14262306a36Sopenharmony_ci resultfn("openat2 with %s argument [misalign=%d] succeeds\n", 14362306a36Sopenharmony_ci test->name, misalign); 14462306a36Sopenharmony_ci else 14562306a36Sopenharmony_ci resultfn("openat2 with %s argument [misalign=%d] fails with %d (%s)\n", 14662306a36Sopenharmony_ci test->name, misalign, test->err, 14762306a36Sopenharmony_ci strerror(-test->err)); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci free(copy); 15062306a36Sopenharmony_ci free(fdpath); 15162306a36Sopenharmony_ci fflush(stdout); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistruct flag_test { 15762306a36Sopenharmony_ci const char *name; 15862306a36Sopenharmony_ci struct open_how how; 15962306a36Sopenharmony_ci int err; 16062306a36Sopenharmony_ci}; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci#define NUM_OPENAT2_FLAG_TESTS 25 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_civoid test_openat2_flags(void) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct flag_test tests[] = { 16762306a36Sopenharmony_ci /* O_TMPFILE is incompatible with O_PATH and O_CREAT. */ 16862306a36Sopenharmony_ci { .name = "incompatible flags (O_TMPFILE | O_PATH)", 16962306a36Sopenharmony_ci .how.flags = O_TMPFILE | O_PATH | O_RDWR, .err = -EINVAL }, 17062306a36Sopenharmony_ci { .name = "incompatible flags (O_TMPFILE | O_CREAT)", 17162306a36Sopenharmony_ci .how.flags = O_TMPFILE | O_CREAT | O_RDWR, .err = -EINVAL }, 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* O_PATH only permits certain other flags to be set ... */ 17462306a36Sopenharmony_ci { .name = "compatible flags (O_PATH | O_CLOEXEC)", 17562306a36Sopenharmony_ci .how.flags = O_PATH | O_CLOEXEC }, 17662306a36Sopenharmony_ci { .name = "compatible flags (O_PATH | O_DIRECTORY)", 17762306a36Sopenharmony_ci .how.flags = O_PATH | O_DIRECTORY }, 17862306a36Sopenharmony_ci { .name = "compatible flags (O_PATH | O_NOFOLLOW)", 17962306a36Sopenharmony_ci .how.flags = O_PATH | O_NOFOLLOW }, 18062306a36Sopenharmony_ci /* ... and others are absolutely not permitted. */ 18162306a36Sopenharmony_ci { .name = "incompatible flags (O_PATH | O_RDWR)", 18262306a36Sopenharmony_ci .how.flags = O_PATH | O_RDWR, .err = -EINVAL }, 18362306a36Sopenharmony_ci { .name = "incompatible flags (O_PATH | O_CREAT)", 18462306a36Sopenharmony_ci .how.flags = O_PATH | O_CREAT, .err = -EINVAL }, 18562306a36Sopenharmony_ci { .name = "incompatible flags (O_PATH | O_EXCL)", 18662306a36Sopenharmony_ci .how.flags = O_PATH | O_EXCL, .err = -EINVAL }, 18762306a36Sopenharmony_ci { .name = "incompatible flags (O_PATH | O_NOCTTY)", 18862306a36Sopenharmony_ci .how.flags = O_PATH | O_NOCTTY, .err = -EINVAL }, 18962306a36Sopenharmony_ci { .name = "incompatible flags (O_PATH | O_DIRECT)", 19062306a36Sopenharmony_ci .how.flags = O_PATH | O_DIRECT, .err = -EINVAL }, 19162306a36Sopenharmony_ci { .name = "incompatible flags (O_PATH | O_LARGEFILE)", 19262306a36Sopenharmony_ci .how.flags = O_PATH | O_LARGEFILE, .err = -EINVAL }, 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* ->mode must only be set with O_{CREAT,TMPFILE}. */ 19562306a36Sopenharmony_ci { .name = "non-zero how.mode and O_RDONLY", 19662306a36Sopenharmony_ci .how.flags = O_RDONLY, .how.mode = 0600, .err = -EINVAL }, 19762306a36Sopenharmony_ci { .name = "non-zero how.mode and O_PATH", 19862306a36Sopenharmony_ci .how.flags = O_PATH, .how.mode = 0600, .err = -EINVAL }, 19962306a36Sopenharmony_ci { .name = "valid how.mode and O_CREAT", 20062306a36Sopenharmony_ci .how.flags = O_CREAT, .how.mode = 0600 }, 20162306a36Sopenharmony_ci { .name = "valid how.mode and O_TMPFILE", 20262306a36Sopenharmony_ci .how.flags = O_TMPFILE | O_RDWR, .how.mode = 0600 }, 20362306a36Sopenharmony_ci /* ->mode must only contain 0777 bits. */ 20462306a36Sopenharmony_ci { .name = "invalid how.mode and O_CREAT", 20562306a36Sopenharmony_ci .how.flags = O_CREAT, 20662306a36Sopenharmony_ci .how.mode = 0xFFFF, .err = -EINVAL }, 20762306a36Sopenharmony_ci { .name = "invalid (very large) how.mode and O_CREAT", 20862306a36Sopenharmony_ci .how.flags = O_CREAT, 20962306a36Sopenharmony_ci .how.mode = 0xC000000000000000ULL, .err = -EINVAL }, 21062306a36Sopenharmony_ci { .name = "invalid how.mode and O_TMPFILE", 21162306a36Sopenharmony_ci .how.flags = O_TMPFILE | O_RDWR, 21262306a36Sopenharmony_ci .how.mode = 0x1337, .err = -EINVAL }, 21362306a36Sopenharmony_ci { .name = "invalid (very large) how.mode and O_TMPFILE", 21462306a36Sopenharmony_ci .how.flags = O_TMPFILE | O_RDWR, 21562306a36Sopenharmony_ci .how.mode = 0x0000A00000000000ULL, .err = -EINVAL }, 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* ->resolve flags must not conflict. */ 21862306a36Sopenharmony_ci { .name = "incompatible resolve flags (BENEATH | IN_ROOT)", 21962306a36Sopenharmony_ci .how.flags = O_RDONLY, 22062306a36Sopenharmony_ci .how.resolve = RESOLVE_BENEATH | RESOLVE_IN_ROOT, 22162306a36Sopenharmony_ci .err = -EINVAL }, 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* ->resolve must only contain RESOLVE_* flags. */ 22462306a36Sopenharmony_ci { .name = "invalid how.resolve and O_RDONLY", 22562306a36Sopenharmony_ci .how.flags = O_RDONLY, 22662306a36Sopenharmony_ci .how.resolve = 0x1337, .err = -EINVAL }, 22762306a36Sopenharmony_ci { .name = "invalid how.resolve and O_CREAT", 22862306a36Sopenharmony_ci .how.flags = O_CREAT, 22962306a36Sopenharmony_ci .how.resolve = 0x1337, .err = -EINVAL }, 23062306a36Sopenharmony_ci { .name = "invalid how.resolve and O_TMPFILE", 23162306a36Sopenharmony_ci .how.flags = O_TMPFILE | O_RDWR, 23262306a36Sopenharmony_ci .how.resolve = 0x1337, .err = -EINVAL }, 23362306a36Sopenharmony_ci { .name = "invalid how.resolve and O_PATH", 23462306a36Sopenharmony_ci .how.flags = O_PATH, 23562306a36Sopenharmony_ci .how.resolve = 0x1337, .err = -EINVAL }, 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* currently unknown upper 32 bit rejected. */ 23862306a36Sopenharmony_ci { .name = "currently unknown bit (1 << 63)", 23962306a36Sopenharmony_ci .how.flags = O_RDONLY | (1ULL << 63), 24062306a36Sopenharmony_ci .how.resolve = 0, .err = -EINVAL }, 24162306a36Sopenharmony_ci }; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_FLAG_TESTS); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci for (int i = 0; i < ARRAY_LEN(tests); i++) { 24662306a36Sopenharmony_ci int fd, fdflags = -1; 24762306a36Sopenharmony_ci char *path, *fdpath = NULL; 24862306a36Sopenharmony_ci bool failed = false; 24962306a36Sopenharmony_ci struct flag_test *test = &tests[i]; 25062306a36Sopenharmony_ci void (*resultfn)(const char *msg, ...) = ksft_test_result_pass; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (!openat2_supported) { 25362306a36Sopenharmony_ci ksft_print_msg("openat2(2) unsupported\n"); 25462306a36Sopenharmony_ci resultfn = ksft_test_result_skip; 25562306a36Sopenharmony_ci goto skip; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci path = (test->how.flags & O_CREAT) ? "/tmp/ksft.openat2_tmpfile" : "."; 25962306a36Sopenharmony_ci unlink(path); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci fd = sys_openat2(AT_FDCWD, path, &test->how); 26262306a36Sopenharmony_ci if (fd < 0 && fd == -EOPNOTSUPP) { 26362306a36Sopenharmony_ci /* 26462306a36Sopenharmony_ci * Skip the testcase if it failed because not supported 26562306a36Sopenharmony_ci * by FS. (e.g. a valid O_TMPFILE combination on NFS) 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci ksft_test_result_skip("openat2 with %s fails with %d (%s)\n", 26862306a36Sopenharmony_ci test->name, fd, strerror(-fd)); 26962306a36Sopenharmony_ci goto next; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (test->err >= 0) 27362306a36Sopenharmony_ci failed = (fd < 0); 27462306a36Sopenharmony_ci else 27562306a36Sopenharmony_ci failed = (fd != test->err); 27662306a36Sopenharmony_ci if (fd >= 0) { 27762306a36Sopenharmony_ci int otherflags; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci fdpath = fdreadlink(fd); 28062306a36Sopenharmony_ci fdflags = fcntl(fd, F_GETFL); 28162306a36Sopenharmony_ci otherflags = fcntl(fd, F_GETFD); 28262306a36Sopenharmony_ci close(fd); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci E_assert(fdflags >= 0, "fcntl F_GETFL of new fd"); 28562306a36Sopenharmony_ci E_assert(otherflags >= 0, "fcntl F_GETFD of new fd"); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* O_CLOEXEC isn't shown in F_GETFL. */ 28862306a36Sopenharmony_ci if (otherflags & FD_CLOEXEC) 28962306a36Sopenharmony_ci fdflags |= O_CLOEXEC; 29062306a36Sopenharmony_ci /* O_CREAT is hidden from F_GETFL. */ 29162306a36Sopenharmony_ci if (test->how.flags & O_CREAT) 29262306a36Sopenharmony_ci fdflags |= O_CREAT; 29362306a36Sopenharmony_ci if (!(test->how.flags & O_LARGEFILE)) 29462306a36Sopenharmony_ci fdflags &= ~O_LARGEFILE; 29562306a36Sopenharmony_ci failed |= (fdflags != test->how.flags); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (failed) { 29962306a36Sopenharmony_ci resultfn = ksft_test_result_fail; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci ksft_print_msg("openat2 unexpectedly returned "); 30262306a36Sopenharmony_ci if (fdpath) 30362306a36Sopenharmony_ci ksft_print_msg("%d['%s'] with %X (!= %X)\n", 30462306a36Sopenharmony_ci fd, fdpath, fdflags, 30562306a36Sopenharmony_ci test->how.flags); 30662306a36Sopenharmony_ci else 30762306a36Sopenharmony_ci ksft_print_msg("%d (%s)\n", fd, strerror(-fd)); 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ciskip: 31162306a36Sopenharmony_ci if (test->err >= 0) 31262306a36Sopenharmony_ci resultfn("openat2 with %s succeeds\n", test->name); 31362306a36Sopenharmony_ci else 31462306a36Sopenharmony_ci resultfn("openat2 with %s fails with %d (%s)\n", 31562306a36Sopenharmony_ci test->name, test->err, strerror(-test->err)); 31662306a36Sopenharmony_cinext: 31762306a36Sopenharmony_ci free(fdpath); 31862306a36Sopenharmony_ci fflush(stdout); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci#define NUM_TESTS (NUM_OPENAT2_STRUCT_VARIATIONS * NUM_OPENAT2_STRUCT_TESTS + \ 32362306a36Sopenharmony_ci NUM_OPENAT2_FLAG_TESTS) 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ciint main(int argc, char **argv) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci ksft_print_header(); 32862306a36Sopenharmony_ci ksft_set_plan(NUM_TESTS); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci test_openat2_struct(); 33162306a36Sopenharmony_ci test_openat2_flags(); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0) 33462306a36Sopenharmony_ci ksft_exit_fail(); 33562306a36Sopenharmony_ci else 33662306a36Sopenharmony_ci ksft_exit_pass(); 33762306a36Sopenharmony_ci} 338