diff --git a/bramble-api/src/main/java/org/briarproject/bramble/util/StringUtils.java b/bramble-api/src/main/java/org/briarproject/bramble/util/StringUtils.java
index b6d082098e0fada88f2712414d224d25325187db..b13fb6030665d260ad7b4cf24b38028035619d89 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/util/StringUtils.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/util/StringUtils.java
@@ -47,7 +47,7 @@ public class StringUtils {
 		try {
 			return s.getBytes("UTF-8");
 		} catch (UnsupportedEncodingException e) {
-			throw new RuntimeException(e);
+			throw new AssertionError(e);
 		}
 	}
 
@@ -63,7 +63,7 @@ public class StringUtils {
 		try {
 			return decoder.decode(buffer).toString();
 		} catch (CharacterCodingException e) {
-			throw new RuntimeException(e);
+			throw new AssertionError(e);
 		}
 	}
 
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/data/BdfReaderImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/data/BdfReaderImpl.java
index d73e5609bdac4b712f23c7fd753b6abfee1fde53..788826a575f4a671c12dc840e293e8da9fdb2c43 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/data/BdfReaderImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/data/BdfReaderImpl.java
@@ -29,6 +29,7 @@ import static org.briarproject.bramble.data.Types.STRING_16;
 import static org.briarproject.bramble.data.Types.STRING_32;
 import static org.briarproject.bramble.data.Types.STRING_8;
 import static org.briarproject.bramble.data.Types.TRUE;
+import static org.briarproject.bramble.util.StringUtils.fromUtf8;
 
 @NotThreadSafe
 @NotNullByDefault
@@ -253,7 +254,7 @@ class BdfReaderImpl implements BdfReader {
 		if (length < 0 || length > maxBufferSize) throw new FormatException();
 		if (length == 0) return "";
 		readIntoBuffer(length);
-		return new String(buf, 0, length, "UTF-8");
+		return fromUtf8(buf, 0, length);
 	}
 
 	private int readStringLength() throws IOException {
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/data/BdfReaderImplFuzzingTest.java b/bramble-core/src/test/java/org/briarproject/bramble/data/BdfReaderImplFuzzingTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..852e66853c91b2fb22aa7b8d3773be6f76ec02cf
--- /dev/null
+++ b/bramble-core/src/test/java/org/briarproject/bramble/data/BdfReaderImplFuzzingTest.java
@@ -0,0 +1,41 @@
+package org.briarproject.bramble.data;
+
+import org.briarproject.bramble.test.BrambleTestCase;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.util.Random;
+
+import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_MAX_BUFFER_SIZE;
+import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_NESTED_LIMIT;
+import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+public class BdfReaderImplFuzzingTest extends BrambleTestCase {
+
+	@Before
+	public void setUp() {
+		assumeTrue(isOptionalTestEnabled(BdfReaderImplFuzzingTest.class));
+	}
+
+	@Test
+	public void testStringFuzzing() throws Exception {
+		Random random = new Random();
+		byte[] buf = new byte[22];
+		ByteArrayInputStream in = new ByteArrayInputStream(buf);
+		for (int i = 0; i < 100_000_000; i++) {
+			random.nextBytes(buf);
+			buf[0] = 0x41; // String with 1-byte length
+			buf[1] = 0x14; // Length 20 bytes
+			in.reset();
+			BdfReaderImpl r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
+					DEFAULT_MAX_BUFFER_SIZE);
+			int length = r.readString().length();
+			assertTrue(length >= 0);
+			assertTrue(length <= 20);
+			assertTrue(r.eof());
+		}
+	}
+}
diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemImpl.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemImpl.java
index c5c9cd710c2a92e1e698c6676b562471be6aa9b5..3bf2867cf6ef806eab82aba4d2a1440862cc4a1d 100644
--- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemImpl.java
+++ b/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemImpl.java
@@ -10,6 +10,10 @@ import org.briarproject.bramble.api.system.Clock;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.locks.Condition;
@@ -22,6 +26,7 @@ import javax.annotation.Nullable;
 import jssc.SerialPortEvent;
 import jssc.SerialPortEventListener;
 
+import static java.nio.charset.CodingErrorAction.IGNORE;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
@@ -35,6 +40,8 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
 
 	private static final Logger LOG =
 			Logger.getLogger(ModemImpl.class.getName());
+
+	private static final Charset US_ASCII = Charset.forName("US-ASCII");
 	private static final int MAX_LINE_LENGTH = 256;
 	private static final int[] BAUD_RATES = {
 		256000, 128000, 115200, 57600, 38400, 19200, 14400, 9600, 4800, 1200
@@ -106,7 +113,7 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
 				throw e;
 			}
 			// Wait for the event thread to receive "OK"
-			boolean success = false;
+			boolean success;
 			try {
 				lock.lock();
 				try {
@@ -353,8 +360,7 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
 		for (int i = 0; i < b.length; i++) {
 			line[lineLen] = b[i];
 			if (b[i] == '\n') {
-				// FIXME: Use CharsetDecoder to catch invalid ASCII
-				String s = new String(line, 0, lineLen, "US-ASCII").trim();
+				String s = toAscii(line, lineLen).trim();
 				lineLen = 0;
 				if (LOG.isLoggable(INFO)) LOG.info("Modem status: " + s);
 				if (s.startsWith("CONNECT")) {
@@ -436,7 +442,7 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
 				throw e;
 			}
 			// Wait for the event thread to receive "CONNECT"
-			boolean success = false;
+			boolean success;
 			try {
 				lock.lock();
 				try {
@@ -461,4 +467,16 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
 			stateChange.release();
 		}
 	}
+
+	private String toAscii(byte[] bytes, int len) {
+		CharsetDecoder decoder = US_ASCII.newDecoder();
+		decoder.onMalformedInput(IGNORE);
+		decoder.onUnmappableCharacter(IGNORE);
+		ByteBuffer buffer = ByteBuffer.wrap(bytes, 0, len);
+		try {
+			return decoder.decode(buffer).toString();
+		} catch (CharacterCodingException e) {
+			throw new AssertionError(e);
+		}
+	}
 }