libfontmanager.so SIGABRT when calculating stringWidth with TextAttributes across multiple threads

XMLWordPrintable

    • 2d
    • linux

      ADDITIONAL SYSTEM INFORMATION :
      Property settings:
          file.encoding = UTF-8
          file.separator = /
          java.class.path =
          java.class.version = 69.0
          java.home = /usr/lib/jvm/java-25-amazon-corretto.aarch64
          java.io.tmpdir = /tmp
          java.library.path = /usr/java/packages/lib
              /usr/lib64
              /lib64
              /lib
              /usr/lib
          java.runtime.name = OpenJDK Runtime Environment
          java.runtime.version = 25.0.2+10-LTS
          java.specification.name = Java Platform API Specification
          java.specification.vendor = Oracle Corporation
          java.specification.version = 25
          java.vendor = Amazon.com Inc.
          java.vendor.url = https://aws.amazon.com/corretto/
          java.vendor.url.bug = https://github.com/corretto/corretto-25/issues/
          java.vendor.version = Corretto-25.0.2.10.1
          java.version = 25.0.2
          java.version.date = 2026-01-20
          java.vm.compressedOopsMode = Zero based
          java.vm.info = mixed mode, sharing
          java.vm.name = OpenJDK 64-Bit Server VM
          java.vm.specification.name = Java Virtual Machine Specification
          java.vm.specification.vendor = Oracle Corporation
          java.vm.specification.version = 25
          java.vm.vendor = Amazon.com Inc.
          java.vm.version = 25.0.2+10-LTS
          jdk.debug = release
          line.separator = \n
          native.encoding = UTF-8
          os.arch = aarch64
          os.name = Linux
          os.version = 6.17.8-orbstack-00308-g8f9c941121b1
          path.separator = :
          stderr.encoding = UTF-8
          stdin.encoding = UTF-8
          stdout.encoding = UTF-8
          sun.arch.data.model = 64
          sun.boot.library.path = /usr/lib/jvm/java-25-amazon-corretto.aarch64/lib
          sun.cpu.endian = little
          sun.io.unicode.encoding = UnicodeLittle
          sun.java.launcher = SUN_STANDARD
          sun.jnu.encoding = UTF-8
          sun.management.compiler = HotSpot 64-Bit Tiered Compilers
          user.dir = /
          user.home = /root
          user.language = en
          user.name = root

      openjdk version "25.0.2" 2026-01-20 LTS
      OpenJDK Runtime Environment Corretto-25.0.2.10.1 (build 25.0.2+10-LTS)
      OpenJDK 64-Bit Server VM Corretto-25.0.2.10.1 (build 25.0.2+10-LTS, mixed mode, sharing)


      A DESCRIPTION OF THE PROBLEM :
      If multiple threads are simultaneously using Font objects with FontAttributes set to calculate stringWidth, libfontmanager.so will try to free an address has not been allocated, causing a SIGABRT to crash the JVM. (If using something like tcmalloc or jemalloc which are stricter about memory violations like this one)

      Also, the JVM does not catch the SIGABRT to create the hs_err.log, so I had to write a C wrapper library to catch it and raise SIGSEGV instead, which is why you see some extra C frames in the hs_err_log after the crash.

      REGRESSION : Last worked in version 21

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Launch 2 virtual threads, both of which create a Font object, enable kerning, and try to use it to calculate the width of a string. It doesn't matter if the Font object is in a shared static field or if they create their own instance.

      The timing doesn't always work with only 2 threads, so if you can't reproduce it, try launching more threads.



      ---------- BEGIN SOURCE ----------
      Java:
      public class FontCrashReproducer {
        public static void main(String[] args) throws InterruptedException {
          var t1 = Thread.ofVirtual().start(() -> handle("oh"));
          var t2 = Thread.ofVirtual().start(() -> handle("no"));
          t1.join();
          t2.join();
          System.out.println("Failed to crash as expected");
        }

        public static Font createFont() {
          try {
            // Loading a random font which happens to be available in the Docker image to simplify reproducer
            var font = Font.createFont(Font.TRUETYPE_FONT, Paths.get("/usr/share/fonts/google-noto-vf/NotoSans[wght].ttf").toFile());
            var map = new HashMap<TextAttribute, Object>();
            map.put(TextAttribute.KERNING, TextAttribute.KERNING_ON); // Disabling Kerning avoids the bug
            return font.deriveFont(map);

          } catch (FontFormatException | IOException e) {
            throw new RuntimeException(e);
          }
        }

        public static void handle(String text) {
          var img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
          var canvas = img.createGraphics();
          canvas.setFont(createFont());
          var fontMetrics = canvas.getFontMetrics();
          fontMetrics.stringWidth(text);
        }
      }


      Dockerfile:
      FROM amazoncorretto:25-al2023
      RUN dnf install gperftools-libs -y
      COPY ./build/libs/app.jar /app.jar
      ENV LD_PRELOAD="/usr/lib64/libtcmalloc.so.4"
      ENTRYPOINT ["java", "-jar", "app.jar"]

      I use amazoncorretto here in my example, but I've also tested with Fedora and OpenJDK 25

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Passing an empty Map to derive font instead of setting any FontAttributes also avoids the crash.

      FREQUENCY :
      ALWAYS

            Assignee:
            Philip Race
            Reporter:
            Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated: