Skip to content

Commit

Permalink
8304441: [macos] Crash when putting invalid unicode char on clipboard
Browse files Browse the repository at this point in the history
Reviewed-by: prr, angorya
  • Loading branch information
kevinrushforth committed Apr 6, 2023
1 parent b6e5737 commit 2b2a7f1
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 2 deletions.
Expand Up @@ -31,6 +31,12 @@
#import "GlassPasteboard.h"
#import "GlassDragSource.h"

// UTF-16 code points for surrogate pairs
#define HIGH_SURROGATE_START 0xD800
#define HIGH_SURROGATE_END 0xDBFF
#define LOW_SURROGATE_START 0xDC00
#define LOW_SURROGATE_END 0xDFFF

//#define VERBOSE
#ifndef VERBOSE
#define LOG(MSG, ...)
Expand All @@ -40,6 +46,40 @@

static NSInteger lastDragSesionNumber = 0;

// Validate the string. Returns false if the string is nil or if
// it contains invalid partial surrogate pairs: a high surrogate
// without an immediately following low surrogate, or conversely,
// a low surrogate without an immediately preceding high surrogate.
static BOOL validate(NSString *data)
{
if (data == nil) {
return NO;
}

BOOL prevHiSurrogate = NO;
NSUInteger i;
for (i = 0; i < [data length]; i++) {
BOOL hiSurrogate = NO;
BOOL loSurrogate = NO;
NSUInteger c = [data characterAtIndex:i];
if (c >= HIGH_SURROGATE_START && c <= HIGH_SURROGATE_END) {
hiSurrogate = YES;
} else if (c >= LOW_SURROGATE_START && c <= LOW_SURROGATE_END) {
loSurrogate = YES;
}

if (loSurrogate && !prevHiSurrogate) {
return NO;
}
if (prevHiSurrogate && !loSurrogate) {
return NO;
}

prevHiSurrogate = hiSurrogate;
}
return !prevHiSurrogate;
}

// Dock puts the data to a custom pasteboard, so dragging from it does not work.
// Copy the contents of the sender PBoard to the DraggingPBoard
void copyToDragPasteboardIfNeeded(id<NSDraggingInfo> sender)
Expand Down Expand Up @@ -213,8 +253,11 @@ static inline void SetNSPasteboardItemValueForUtf(JNIEnv *env, NSPasteboardItem
string = [NSString stringWithCharacters:(UniChar *)chars length:(NSUInteger)(*env)->GetStringLength(env, jValue)];
(*env)->ReleaseStringChars(env, jValue, chars);
}
[item setString:string forType:utf];

//NSLog(@" SetValue(string): %@, ForUtf: %@", string, utf);
if (validate(string)) {
[item setString:string forType:utf];
}
}
else
{
Expand Down Expand Up @@ -286,7 +329,14 @@ static inline void SetNSPasteboardItemValueForUtf(JNIEnv *env, NSPasteboardItem
}
(*env)->ReleaseStringChars(env, jUtf, chars);
}
SetNSPasteboardItemValueForUtf(env, item, jObject, utf);
// Setting pasteboard can throw exception. It shouldn't
// happen if we validate the data, but in case it does,
// we will catch the exception and log a warning
@try {
SetNSPasteboardItemValueForUtf(env, item, jObject, utf);
} @catch (NSException *exception) {
NSLog(@"WARNING: %@: %@ ",exception.name, exception.reason);
}
}
else
{
Expand Down
Expand Up @@ -32,6 +32,7 @@
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import javafx.scene.input.Clipboard;
Expand Down Expand Up @@ -117,6 +118,68 @@ public void testPasteUTF8String() throws Exception {
assertEquals(text, clipboard.getContent(DataFormat.PLAIN_TEXT)));
}

private void testClipboard(List<List<Integer>> codePointsLists, boolean checkResults) {
codePointsLists.stream().forEach(codePointsList -> {
int[] codePoints = codePointsList.stream()
.mapToInt(Integer::intValue)
.toArray();
String text = new String(codePoints, 0, codePoints.length);
ClipboardContent content = new ClipboardContent();
content.put(DataFormat.PLAIN_TEXT, text);
Util.runAndWait(() -> clipboard.setContent(content));
if (checkResults) {
Util.runAndWait(() ->
assertEquals(text, clipboard.getContent(DataFormat.PLAIN_TEXT)));
}
});
}

/*
* @test 8304441
*
* Test bad strings with mismatched halves of surrogate pairs.
*/
@Test
public void testCopyBadSurrogate() {
List<List<Integer>> codePointsLists = List.of(
List.of(0xD800), // High
List.of(0xDBFF), // High
List.of(0xD83D), // High
List.of(0xDC00), // Low
List.of(0xDFFF), // Low
List.of(0xD800, 0xDBFF), // High, High
List.of(0xDC00, 0xDFFF), // Low, Low
List.of(0xDC00, 0xD800), // Low, High
List.of(0xDFFF, 0xDBFF), // Low, High
List.of(0x1F600, 0xD800), // High, Low, High
List.of(0xD800, 0x1F600), // High, High, Low
List.of(0x41, 0xD83D, 0x5A), // 'A', High, 'Z'
List.of(0x41, 0xDDDD, 0x5A) // 'A', Low, 'Z'
);

testClipboard(codePointsLists, false);
}

/*
* @test 8304441
*
* Test good strings with matched surrogate pairs.
*/
@Test
public void testCopyPasteSurrogatePairs() {
List<List<Integer>> codePointsLists = List.of(
List.of(0xD800, 0xDC00), // High, Low
List.of(0xDBFF, 0xDFFF), // High, Low
List.of(0x1F600),
List.of(0x1F603),
List.of(0x1F976),
List.of(0x1F600, 0x1F603, 0x1F976),
List.of(0x41, 0x1F600, 0x1F603, 0x1F976, 0x5A)
);

testClipboard(codePointsLists, true);
}

/*
* @bug 8274929
*/
Expand Down

3 comments on commit 2b2a7f1

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinrushforth
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/tag 21+12

@openjdk
Copy link

@openjdk openjdk bot commented on 2b2a7f1 Apr 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinrushforth The tag 21+12 was successfully created.

Please sign in to comment.