From bb978f57ca6537036c24b310269b6e5d75bf9a0d Mon Sep 17 00:00:00 2001 From: Daniel Widdis Date: Wed, 21 Apr 2021 20:20:50 -0700 Subject: [PATCH] CFStringRef#stringValue buffer needs space for null byte --- CHANGES.md | 2 +- .../sun/jna/platform/mac/CoreFoundation.java | 4 +++ .../jna/platform/mac/CoreFoundationTest.java | 31 ++++++++++--------- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3f68404940..9ec75e9440 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,7 +14,7 @@ Features Bug Fixes --------- - +* [#1343](https://github.com/java-native-access/jna/issues/1343): `c.s.j.p.mac.CoreFoundation.CFStringRef#stringValue` buffer needs space for null byte - [@dbwiddis](https://github.com/dbwiddis). Release 5.8.0 ============= diff --git a/contrib/platform/src/com/sun/jna/platform/mac/CoreFoundation.java b/contrib/platform/src/com/sun/jna/platform/mac/CoreFoundation.java index 5e9b26605b..d917d83eb2 100644 --- a/contrib/platform/src/com/sun/jna/platform/mac/CoreFoundation.java +++ b/contrib/platform/src/com/sun/jna/platform/mac/CoreFoundation.java @@ -487,14 +487,18 @@ public static CFStringRef createCFString(String s) { * failed. */ public String stringValue() { + // Get number of characters CFIndex length = INSTANCE.CFStringGetLength(this); if (length.longValue() == 0) { return ""; } + // Calculate maximum possible size in UTF8 bytes CFIndex maxSize = INSTANCE.CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8); if (maxSize.intValue() == kCFNotFound) { throw new StringIndexOutOfBoundsException("CFString maximum number of bytes exceeds LONG_MAX."); } + // Increment size by 1 for a null byte + maxSize.setValue(maxSize.longValue() + 1); Memory buf = new Memory(maxSize.longValue()); if (0 != INSTANCE.CFStringGetCString(this, buf, maxSize, kCFStringEncodingUTF8)) { return buf.getString(0, "UTF8"); diff --git a/contrib/platform/test/com/sun/jna/platform/mac/CoreFoundationTest.java b/contrib/platform/test/com/sun/jna/platform/mac/CoreFoundationTest.java index f6ff8e966f..9bd2e68843 100644 --- a/contrib/platform/test/com/sun/jna/platform/mac/CoreFoundationTest.java +++ b/contrib/platform/test/com/sun/jna/platform/mac/CoreFoundationTest.java @@ -33,6 +33,7 @@ import static org.junit.Assert.fail; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; import java.util.Random; @@ -62,25 +63,27 @@ public class CoreFoundationTest { @Test public void testCFStringRef() throws UnsupportedEncodingException { - String awesome = "ǝɯosǝʍɐ sı ∀Nſ"; // Unicode - CFStringRef cfAwesome = CFStringRef.createCFString(awesome); - assertEquals(awesome.length(), CF.CFStringGetLength(cfAwesome).intValue()); - assertEquals(awesome, cfAwesome.stringValue()); - assertEquals(CoreFoundation.STRING_TYPE_ID, cfAwesome.getTypeID()); - - byte[] awesomeArr = awesome.getBytes("UTF-8"); - Memory mem = new Memory(awesomeArr.length + 1); + // Create a unicode string of a single 3-byte character + byte[] alaf = { (byte) 0xe0, (byte) 0xa0, (byte) 0x80 }; + String utf8Str = new String(alaf, StandardCharsets.UTF_8); + CFStringRef cfStr = CFStringRef.createCFString(utf8Str); + assertEquals(utf8Str.length(), CF.CFStringGetLength(cfStr).intValue()); + assertEquals(utf8Str, cfStr.stringValue()); + assertEquals(CoreFoundation.STRING_TYPE_ID, cfStr.getTypeID()); + + byte[] utf8Arr = utf8Str.getBytes("UTF-8"); + Memory mem = new Memory(utf8Arr.length + 1); mem.clear(); assertNotEquals(0, - CF.CFStringGetCString(cfAwesome, mem, new CFIndex(mem.size()), CoreFoundation.kCFStringEncodingUTF8)); - byte[] awesomeBytes = mem.getByteArray(0, (int) mem.size() - 1); - assertArrayEquals(awesomeArr, awesomeBytes); + CF.CFStringGetCString(cfStr, mem, new CFIndex(mem.size()), CoreFoundation.kCFStringEncodingUTF8)); + byte[] utf8Bytes = mem.getByteArray(0, (int) mem.size() - 1); + assertArrayEquals(utf8Arr, utf8Bytes); // Essentially a toString, can't rely on format but should contain the string - CFStringRef desc = CF.CFCopyDescription(cfAwesome); - assertTrue(desc.stringValue().contains(awesome)); + CFStringRef desc = CF.CFCopyDescription(cfStr); + assertTrue(desc.stringValue().contains(utf8Str)); desc.release(); - cfAwesome.release(); + cfStr.release(); CFStringRef cfEmpty = CFStringRef.createCFString(""); assertTrue(cfEmpty.stringValue().equals(""));