bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

jit/cache tests: Avoid test failure on CentOS, *BSD, macOS


From: Bruno Haible
Subject: jit/cache tests: Avoid test failure on CentOS, *BSD, macOS
Date: Mon, 08 Jan 2024 17:25:24 +0100

Testing the jit/cache module on various x86_64 platforms, I see test failures:

- On CentOS. Here the problem is that pagealign_alloc returns a piece of memory
  with PROT_READ | PROT_WRITE access. The mprotect that wants to install
  a PROT_EXEC access then fails. The solution is to give the right protection
  flags to mmap(), and not invoke mprotect().

- On *BSD and macOS. Here, the solution that works for CentOS does not work:
  mmap (..., PROT_READ | PROT_WRITE | PROT_EXEC, ...) fails, due to security
  reasons. The solution is to use two different mmap() invocations for the
  same memory.

Also, some of the #if conditions were incorrect:
  - '#if defined __hppa__' is needed instead of '#if __hppa__'.
  - Allow for __hppa__ to be set in __hppa64__ mode, and allow for __ia64__
    to be set in 32-bit ia64 mode.


2024-01-08  Bruno Haible  <bruno@clisp.org>

        jit/cache tests: Avoid test failure on CentOS, *BSD, macOS.
        * tests/jit/test-cache.c: Include <fcntl.h>, <stdio.h>, <stdlib.h>.
        Don't include pagealign_alloc.h. Include clean-temp-simple.h. Don't test
        HAVE_MPROTECT.
        (struct func): Fix #if conditions.
        (main): Don't invoke pagealign_xalloc and mprotect. Instead, invoke 
mmap,
        using a temporary file if needed.
        * modules/jit/cache-tests (Files): Add m4/mmap-anon.m4.
        (Depends-on): Add clean-temp-simple. Remove pagealign_alloc.
        (configure.ac): Invoke gl_FUNC_MMAP_ANON. Don't test for mprotect.

diff --git a/modules/jit/cache-tests b/modules/jit/cache-tests
index 6a713f8bb9..e4229476c8 100644
--- a/modules/jit/cache-tests
+++ b/modules/jit/cache-tests
@@ -1,19 +1,20 @@
 Files:
 tests/jit/test-cache.c
 tests/macros.h
+m4/mmap-anon.m4
 
 Status:
 unportable-test
 
 Depends-on:
+clean-temp-simple
 getpagesize
 host-cpu-c-abi
-pagealign_alloc
 stdint
 
 configure.ac:
 AC_CHECK_HEADERS_ONCE([sys/mman.h])
-AC_CHECK_FUNCS_ONCE([mprotect])
+gl_FUNC_MMAP_ANON
 
 Makefile.am:
 TESTS += test-cache
diff --git a/tests/jit/test-cache.c b/tests/jit/test-cache.c
index 8ced2e819c..35d63b7842 100644
--- a/tests/jit/test-cache.c
+++ b/tests/jit/test-cache.c
@@ -1,6 +1,6 @@
 /* Test clear_cache.
 
-   Copyright 2023-2024 Free Software Foundation, Inc.
+   Copyright 2020-2024 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -17,16 +17,25 @@
 
 #include <config.h>
 
+/* Specification.  */
 #include <jit/cache.h>
 
-#include <pagealign_alloc.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#if HAVE_SYS_MMAN_H && HAVE_MPROTECT
+#if HAVE_SYS_MMAN_H
 # include <sys/mman.h>
 #endif
 
+#if defined __APPLE__ && defined __MACH__ /* only needed on macOS */
+# define KEEP_TEMP_FILE_VISIBLE
+/* Support for temporary files that are cleaned up automatically. */
+# include "clean-temp-simple.h"
+#endif
+
 #include "macros.h"
 
 /*  On most platforms, function pointers are just a pointer to the
@@ -43,33 +52,37 @@ struct func
   void *static_chain;
 };
 #elif defined __ia64__
+# if defined __ia64_ilp32__
 struct func
 {
   void *code_address;
+  void *unused1;
   void *global_pointer;
+  void *unused2;
 };
-#elif defined __ia64_ilp32__
+# else
 struct func
 {
   void *code_address;
-  void *unused1;
   void *global_pointer;
-  void *unused2;
 };
-#elif __hppa__
+# endif
+#elif defined __hppa__
+# if defined __hppa64__
 struct func
 {
+  void *some_other_code_address;
+  void *some_other_pic_base;
   void *code_address;
   void *pic_base;
 };
-#elif __hppa64__
+# else
 struct func
 {
-  void *some_other_code_address;
-  void *some_other_pic_base;
   void *code_address;
   void *pic_base;
 };
+# endif
 #else
 # undef CODE
 # define CODE(fn) ((*(void **) (&fn)))
@@ -94,27 +107,81 @@ return2 (void)
 int
 main ()
 {
-#if !(HAVE_SYS_MMAN_H && HAVE_MPROTECT)
+#if !HAVE_SYS_MMAN_H
   return 77;
 #else
   int const pagesize = getpagesize ();
-  unsigned char *start = pagealign_xalloc (pagesize);
-  unsigned char *end = start + pagesize;
-
-  /* We have to call `mprotect' before the tests because on some
-     platforms `mprotect' invalidates the caches.  */
-  mprotect (start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC);
+  int const mapping_size = 1 * pagesize;
+  /* Bounds of an executable memory region.  */
+  char *start;
+  char *end;
+  /* Start of a writable memory region.  */
+  char *start_rw;
+
+  /* Initialization.  */
+  {
+# ifdef HAVE_MAP_ANONYMOUS
+    int flags = MAP_ANONYMOUS | MAP_PRIVATE;
+    int fd = -1;
+# else
+    int flags = MAP_FILE | MAP_PRIVATE;
+    int fd = open ("/dev/zero", O_RDONLY | O_CLOEXEC, 0666);
+    if (fd < 0)
+      return 1;
+# endif
+    start = mmap (NULL, mapping_size, PROT_READ | PROT_WRITE | PROT_EXEC,
+                  flags, fd, 0);
+    if (start != (char *) (-1))
+      {
+        /* A platform that allows a mmap'ed memory region to be simultaneously
+           writable and executable.  */
+        start_rw = start;
+      }
+    else
+      {
+        /* A platform which requires the writable mapping and the executable
+           mapping to be separate: macOS, FreeBSD, NetBSD, OpenBSD.  */
+        fprintf (stderr, "simple mmap failed, using separate mappings\n");
+        char filename[100];
+        sprintf (filename,
+                 "%s/gnulib-test-cache-%u-%d-%ld",
+                 "/tmp", (unsigned int) getuid (), (int) getpid (), random ());
+# ifdef KEEP_TEMP_FILE_VISIBLE
+        if (register_temporary_file (filename) < 0)
+          return 2;
+# endif
+        fd = open (filename, O_CREAT | O_RDWR | O_TRUNC, 0700);
+        if (fd < 0)
+          return 3;
+# ifndef KEEP_TEMP_FILE_VISIBLE
+        /* Remove the file from the file system as soon as possible, to make
+           sure there is no leftover after this process terminates or crashes.
+           On macOS 11.2, this does not work: It would make the mmap call 
below,
+           with arguments PROT_READ|PROT_EXEC and MAP_SHARED, fail. */
+        unlink (filename);
+# endif
+        if (ftruncate (fd, mapping_size) < 0)
+          return 4;
+        start = mmap (NULL, mapping_size, PROT_READ | PROT_EXEC, MAP_SHARED,
+                      fd, 0);
+        start_rw = mmap (NULL, mapping_size, PROT_READ | PROT_WRITE, 
MAP_SHARED,
+                         fd, 0);
+        if (start == (char *) (-1) || start_rw == (char *) (-1))
+          return 5;
+      }
+    end = start + mapping_size;
+  }
 
   int (*f) (void) = return1;
   CODE (f) = start;
 
   /* We assume that the code is not longer than 64 bytes and that we
      can access the full 64 bytes for reading.  */
-  memcpy (start, return1, 64);
+  memcpy (start_rw, return1, 64);
   clear_cache (start, end);
   ASSERT (f () == 1);
 
-  memcpy (start, return2, 64);
+  memcpy (start_rw, return2, 64);
   clear_cache (start, end);
   ASSERT (f () == 2);
 






reply via email to

[Prev in Thread] Current Thread [Next in Thread]