Signed-off-by: Aleksa Sarai <cyp...@cyphar.com>
---
 tools/testing/selftests/openat2/openat2_test.c | 122 ++++++++++++++++++++++++-
 1 file changed, 120 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/openat2/openat2_test.c 
b/tools/testing/selftests/openat2/openat2_test.c
index 4ca175a16ad6..8afb41d0958a 100644
--- a/tools/testing/selftests/openat2/openat2_test.c
+++ b/tools/testing/selftests/openat2/openat2_test.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 /*
  * Author: Aleksa Sarai <cyp...@cyphar.com>
- * Copyright (C) 2018-2019 SUSE LLC.
+ * Copyright (C) 2018-2024 SUSE LLC.
  */
 
 #define _GNU_SOURCE
@@ -29,6 +29,14 @@
 #define        O_LARGEFILE 0x8000
 #endif
 
+#ifndef CHECK_FIELDS
+#define CHECK_FIELDS (1ULL << 63)
+#endif
+
+#ifndef EEXTSYS_NOOP
+#define EEXTSYS_NOOP 134
+#endif
+
 struct open_how_ext {
        struct open_how inner;
        uint32_t extra1;
@@ -45,6 +53,114 @@ struct struct_test {
        int err;
 };
 
+#define NUM_OPENAT2_CHECK_FIELDS_TESTS 1
+#define NUM_OPENAT2_CHECK_FIELDS_VARIATIONS 13
+
+static bool check(bool *failed, bool pred)
+{
+       *failed |= pred;
+       return pred;
+}
+
+static void test_openat2_check_fields(void)
+{
+       int misalignments[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 17, 87 };
+
+       for (int i = 0; i < ARRAY_LEN(misalignments); i++) {
+               int fd, misalign = misalignments[i];
+               bool failed = false;
+               char *fdpath = NULL;
+               void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
+
+               struct open_how_ext how_ext = {}, *how_copy = &how_ext;
+               void *copy = NULL;
+
+               if (!openat2_supported) {
+                       ksft_print_msg("openat2(2) unsupported\n");
+                       resultfn = ksft_test_result_skip;
+                       goto skip;
+               }
+
+               if (misalign) {
+                       /*
+                        * Explicitly misalign the structure copying it with
+                        * the given (mis)alignment offset. The other data is
+                        * set to zero and we verify this afterwards to make
+                        * sure CHECK_FIELDS doesn't write outside the buffer.
+                        */
+                       copy = malloc(misalign*2 + sizeof(how_ext));
+                       how_copy = copy + misalign;
+                       memset(copy, 0x00, misalign*2 + sizeof(how_ext));
+                       memcpy(how_copy, &how_ext, sizeof(how_ext));
+               }
+
+               fd = raw_openat2(AT_FDCWD, ".", how_copy, CHECK_FIELDS | 
sizeof(*how_copy));
+               if (check(&failed, (fd != -EEXTSYS_NOOP)))
+                       ksft_print_msg("openat2(CHECK_FIELDS) returned wrong 
error code: %d (%s)",
+                                      fd, strerror(-fd));
+               if (fd >= 0) {
+                       fdpath = fdreadlink(fd);
+                       close(fd);
+               }
+
+               if (failed) {
+                       ksft_print_msg("openat2(CHECK_FIELDS) unexpectedly 
returned ");
+                       if (fdpath)
+                               ksft_print_msg("%d['%s']\n", fd, fdpath);
+                       else
+                               ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
+               }
+
+               if (check(&failed, !(how_copy->inner.flags & O_PATH)))
+                       ksft_print_msg("openat2(CHECK_FIELDS) returned flags is 
missing O_PATH (0x%.16x): 0x%.16llx\n",
+                                      O_PATH, how_copy->inner.flags);
+
+               if (check(&failed, (how_copy->inner.mode != 07777)))
+                       ksft_print_msg("openat2(CHECK_FIELDS) returned mode is 
invalid (0o%o): 0o%.4llo\n",
+                                      07777, how_copy->inner.mode);
+
+               if (check(&failed, !(how_copy->inner.resolve & 
RESOLVE_IN_ROOT)))
+                       ksft_print_msg("openat2(CHECK_FIELDS) returned resolve 
flags is missing RESOLVE_IN_ROOT (0x%.16x): 0x%.16llx\n",
+                                      RESOLVE_IN_ROOT, 
how_copy->inner.resolve);
+
+               /* Verify that the buffer space outside the struct wasn't 
written to. */
+               if (check(&failed, how_copy->extra1 != 0))
+                       ksft_print_msg("openat2(CHECK_FIELDS) touched a byte 
outside open_how (extra1): 0x%x\n",
+                                      how_copy->extra1);
+               if (check(&failed, how_copy->extra2 != 0))
+                       ksft_print_msg("openat2(CHECK_FIELDS) touched a byte 
outside open_how (extra2): 0x%x\n",
+                                      how_copy->extra2);
+               if (check(&failed, how_copy->extra3 != 0))
+                       ksft_print_msg("openat2(CHECK_FIELDS) touched a byte 
outside open_how (extra3): 0x%x\n",
+                                      how_copy->extra3);
+
+               if (misalign) {
+                       for (size_t i = 0; i < misalign; i++) {
+                               char *p = copy + i;
+                               if (check(&failed, *p != '\x00'))
+                                       ksft_print_msg("openat2(CHECK_FIELDS) 
touched a byte outside the size: buffer[%ld] = 0x%.2x\n",
+                                                      p - (char *) copy, *p);
+                       }
+                       for (size_t i = 0; i < misalign; i++) {
+                               char *p = copy + misalign + sizeof(how_ext) + i;
+                               if (check(&failed, *p != '\x00'))
+                                       ksft_print_msg("openat2(CHECK_FIELDS) 
touched a byte outside the size: buffer[%ld] = 0x%.2x\n",
+                                                      p - (char *) copy, *p);
+                       }
+               }
+
+               if (failed)
+                       resultfn = ksft_test_result_fail;
+
+skip:
+               resultfn("openat2(CHECK_FIELDS) [misalign=%d]\n", misalign);
+
+               free(copy);
+               free(fdpath);
+               fflush(stdout);
+       }
+}
+
 #define NUM_OPENAT2_STRUCT_TESTS 7
 #define NUM_OPENAT2_STRUCT_VARIATIONS 13
 
@@ -320,7 +436,8 @@ void test_openat2_flags(void)
        }
 }
 
-#define NUM_TESTS (NUM_OPENAT2_STRUCT_VARIATIONS * NUM_OPENAT2_STRUCT_TESTS + \
+#define NUM_TESTS (NUM_OPENAT2_CHECK_FIELDS_TESTS * 
NUM_OPENAT2_CHECK_FIELDS_VARIATIONS + \
+                  NUM_OPENAT2_STRUCT_VARIATIONS * NUM_OPENAT2_STRUCT_TESTS + \
                   NUM_OPENAT2_FLAG_TESTS)
 
 int main(int argc, char **argv)
@@ -328,6 +445,7 @@ int main(int argc, char **argv)
        ksft_print_header();
        ksft_set_plan(NUM_TESTS);
 
+       test_openat2_check_fields();
        test_openat2_struct();
        test_openat2_flags();
 

-- 
2.46.0


Reply via email to