From fb0c873b3ab030fcc298a8a01f170a8a8512ce75 Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Wed, 18 Sep 2024 13:09:42 -0700 Subject: [PATCH] ICU4J: Throw parse error for lone surrogates --- .../com/ibm/icu/message2/InputSource.java | 7 +++++ .../java/com/ibm/icu/message2/MFParser.java | 10 ++++++- .../dev/test/message2/MessageFormat2Test.java | 30 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/icu4j/main/core/src/main/java/com/ibm/icu/message2/InputSource.java b/icu4j/main/core/src/main/java/com/ibm/icu/message2/InputSource.java index 8c89c93bf51a..167ace313cde 100644 --- a/icu4j/main/core/src/main/java/com/ibm/icu/message2/InputSource.java +++ b/icu4j/main/core/src/main/java/com/ibm/icu/message2/InputSource.java @@ -29,6 +29,13 @@ int peekChar() { return buffer.charAt(cursor); } + // Used when checking for unpaired surrogates + int readChar() { + int ch = peekChar(); + cursor++; + return (char) ch; + } + int readCodePoint() { // TODO: remove this? // START Detect possible infinite loop diff --git a/icu4j/main/core/src/main/java/com/ibm/icu/message2/MFParser.java b/icu4j/main/core/src/main/java/com/ibm/icu/message2/MFParser.java index a5c826f87397..791b45e864ee 100644 --- a/icu4j/main/core/src/main/java/com/ibm/icu/message2/MFParser.java +++ b/icu4j/main/core/src/main/java/com/ibm/icu/message2/MFParser.java @@ -113,9 +113,17 @@ private MFDataModel.PatternPart getPatternPart() throws MFParseException { } } - private String getText() { + private String getText() throws MFParseException { StringBuilder result = new StringBuilder(); while (true) { + int ch = input.readChar(); + // Check for unmatched surrogates + checkCondition(!(Character.isHighSurrogate((char) ch) + && (input.atEnd() + || !Character.isLowSurrogate((char) input.peekChar()))), + "Unpaired high surrogate"); + checkCondition (!Character.isLowSurrogate((char) ch), "Unpaired low surrogate"); + input.backup(1); int cp = input.readCodePoint(); switch (cp) { case EOF: diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/message2/MessageFormat2Test.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/message2/MessageFormat2Test.java index 6ac37d6085fd..fece39bfa603 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/message2/MessageFormat2Test.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/message2/MessageFormat2Test.java @@ -46,6 +46,36 @@ public void test() { mf2.formatToString(Args.NONE)); } + @Test + public void testHighLoneSurrogate() { + try { + MessageFormatter mf2 = MessageFormatter.builder() + .setPattern("\uda02").build(); + assertEquals("testHighLoneSurrogate: expected to throw, but didn't", + false, true); + } catch (IllegalArgumentException e) { + // Parse error was thrown, as expected + } catch (Exception e) { + assertEquals("testHighLoneSurrogate: expected IllegalArgumentException " + + "but threw " + e.toString(), false, true); + } + } + + @Test + public void testLowLoneSurrogate() { + try { + MessageFormatter mf2 = MessageFormatter.builder() + .setPattern("\udc02").build(); + assertEquals("testLowLoneSurrogate: expected to throw, but didn't", + false, true); + } catch (IllegalArgumentException e) { + // Parse error was thrown, as expected + } catch (Exception e) { + assertEquals("testLowLoneSurrogate: expected IllegalArgumentException " + + "but threw " + e.toString(), false, true); + } + } + @Test public void testDateFormat() { Date expiration = new Date(2022 - 1900, java.util.Calendar.OCTOBER, 27);