Skip to content

Commit

Permalink
8246348: Crash in libpango on Ubuntu 20.04 with some unicode chars
Browse files Browse the repository at this point in the history
Reviewed-by: prr, kcr
  • Loading branch information
Johan Vos committed Jun 15, 2020
1 parent b200891 commit bf2e972
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class OSPango {
/* Miscellaneous (glib, fontconfig) */
static final native long g_utf8_offset_to_pointer(long str, long offset);
static final native long g_utf8_pointer_to_offset(long str, long pos);
static final native long g_utf8_strlen(long str, long max);
static final native long g_utf16_to_utf8(char[] str);
static final native void g_free(long ptr);
static final native int g_list_length(long list);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
import com.sun.javafx.text.GlyphLayout;
import com.sun.javafx.text.TextRun;

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;

class PangoGlyphLayout extends GlyphLayout {
private static final long fontmap;

Expand Down Expand Up @@ -80,9 +84,8 @@ private boolean check(long checkValue, String message, long context, long desc,
return true;
}

private long str = 0L;
private Map<TextRun, Long> runUtf8 = new LinkedHashMap<>();
public void layout(TextRun run, PGFont font, FontStrike strike, char[] text) {

/* Create the pango font and attribute list */
FontResource fr = font.getFontResource();
boolean composite = fr instanceof CompositeFontResource;
Expand Down Expand Up @@ -126,17 +129,20 @@ public void layout(TextRun run, PGFont font, FontStrike strike, char[] text) {
OSPango.pango_attr_list_insert(attrList, attr);
}

if (str == 0L) {
str = OSPango.g_utf16_to_utf8(text);
Long str = runUtf8.get(run);
if (str == null) {
char[] rtext = Arrays.copyOfRange(text, run.getStart(), run.getEnd());
str = OSPango.g_utf16_to_utf8(rtext);
if (check(str, "Failed allocating UTF-8 buffer.", context, desc, attrList)) {
return;
}
runUtf8.put(run, str);
}

/* Itemize */
long start = OSPango.g_utf8_offset_to_pointer(str, run.getStart());
long end = OSPango.g_utf8_offset_to_pointer(str, run.getEnd());
long runs = OSPango.pango_itemize(context, str, (int)(start - str), (int)(end - start), attrList, 0);
long utflen = OSPango.g_utf8_strlen(str,-1);
long end = OSPango.g_utf8_offset_to_pointer(str, utflen);
long runs = OSPango.pango_itemize(context, str, 0, (int)(end - str), attrList, 0);

if (runs != 0) {
/* Shape all PangoItem into PangoGlyphString */
Expand Down Expand Up @@ -199,9 +205,9 @@ public void layout(TextRun run, PGFont font, FontStrike strike, char[] text) {
@Override
public void dispose() {
super.dispose();
if (str != 0L) {
for (Long str: runUtf8.values()) {
OSPango.g_free(str);
str = 0L;
}
runUtf8.clear();
}
}
7 changes: 7 additions & 0 deletions modules/javafx.graphics/src/main/native-font/pango.c
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,13 @@ JNIEXPORT jlong JNICALL OS_NATIVE(g_1utf8_1pointer_1to_1offset)
return (jlong)g_utf8_pointer_to_offset((const gchar *)str, (const gchar *)pos);
}

JNIEXPORT jlong JNICALL OS_NATIVE(g_1utf8_1strlen)
(JNIEnv *env, jclass that, jlong str, jlong pos)
{
if (!str) return 0;
return (jlong)g_utf8_strlen((const gchar *)str, (const gchar *)pos);
}

JNIEXPORT jlong JNICALL OS_NATIVE(g_1utf16_1to_1utf8)
(JNIEnv *env, jclass that, jcharArray str)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package test.com.sun.javafx.font.freetype;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;

import org.junit.Test;
import org.junit.AfterClass;
import org.junit.BeforeClass;

import junit.framework.AssertionFailedError;
import static test.util.Util.TIMEOUT;

import static org.junit.Assert.*;

/**
* Test program for UTF16 to UTF8 conversion and Pango
*/
public class PangoTest {

static CountDownLatch launchLatch = new CountDownLatch(1);

static MyApp myApp;
static Pane pane;

public static class MyApp extends Application {

Stage stage = null;

public MyApp() {
super();
}

@Override
public void init() {
myApp = this;
}

@Override
public void start(Stage primaryStage) throws Exception {
this.stage = primaryStage;
pane = new VBox(10);
Scene scene = new Scene(pane, 400, 200);
stage.setScene(scene);
stage.addEventHandler(WindowEvent.WINDOW_SHOWN, e -> Platform.runLater(launchLatch::countDown));
stage.show();
}
}

@BeforeClass
public static void setupOnce() throws Exception {
// Start the Application
new Thread(() -> Application.launch(MyApp.class, (String[]) null)).start();
assertTrue("Timeout waiting for Application to launch",
launchLatch.await(TIMEOUT, TimeUnit.MILLISECONDS));
assertEquals(0, launchLatch.getCount());
}

@AfterClass
public static void teardownOnce() {
Platform.exit();
}

private void addTextToPane(Text text) throws Exception {
final CountDownLatch rDone = new CountDownLatch(1);
Platform.runLater(() -> {
text.layoutYProperty().addListener(inv -> {
rDone.countDown();
});
pane.getChildren().add(text);
});
assertTrue("Timeout waiting for runLater", rDone.await(TIMEOUT, TimeUnit.MILLISECONDS));
}

@Test
public void testZeroChar() throws Exception {
String FULL_UNICODE_SET;
StringBuilder builder = new StringBuilder();
for (int character = 0; character < 10000; character++) {
char[] chars = Character.toChars(character);
builder.append(chars);
}
FULL_UNICODE_SET = builder.toString();
Text text = new Text(FULL_UNICODE_SET);
addTextToPane(text);
}

@Test
public void testSurrogatePair() throws Exception {
StringBuilder builder = new StringBuilder();
builder.append(Character.toChars(55358));
builder.append(Character.toChars(56605));
builder.append(Character.toChars(8205));

Text text = new Text(builder.toString());
addTextToPane(text);
}
}

0 comments on commit bf2e972

Please sign in to comment.