diff options
| author | Lena <lena@omega> | 2026-08-01 00:00:00 +0000 |
|---|---|---|
| committer | Lena <lena@omega> | 2026-08-01 00:00:00 +0000 |
| commit | 852a8a00273c9128efeed0217a8e5d3fcd8bf780 (patch) | |
| tree | c72f959731fa3c7c809cf1a0222363b6c9c9b03b /app/src/main/java/invalid/lena/scrcpy/Sync.java | |
| parent | f379b93d52bf0dbd3816ec3f64d165314771d2a6 (diff) | |
| download | scrcpy-android-852a8a00273c9128efeed0217a8e5d3fcd8bf780.tar.gz | |
app: harden mirroring lifecycle and state
Diffstat (limited to 'app/src/main/java/invalid/lena/scrcpy/Sync.java')
| -rw-r--r-- | app/src/main/java/invalid/lena/scrcpy/Sync.java | 54 |
1 files changed, 48 insertions, 6 deletions
diff --git a/app/src/main/java/invalid/lena/scrcpy/Sync.java b/app/src/main/java/invalid/lena/scrcpy/Sync.java index 6e37523..b7bb4b5 100644 --- a/app/src/main/java/invalid/lena/scrcpy/Sync.java +++ b/app/src/main/java/invalid/lena/scrcpy/Sync.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; +import java.util.Objects; // Pure-java adb sync v1 SEND framing. Extracted from Server.push() so // it can be unit-tested without an AdbStream or any android coupling. @@ -23,6 +24,7 @@ public final class Sync { // FAIL responses carry a short human-readable message; cap what a // corrupt or hostile length field can make us allocate. private static final int MAX_FAIL_MSG = 4 * 1024; + private static final int MAX_PATH_BYTES = 1024; private Sync() {} @@ -30,10 +32,22 @@ public final class Sync { // already-open `out` and `in` of an adb sync stream. Returns the // total payload byte count. public static long push(InputStream src, OutputStream out, InputStream in, - String remotePath, int mode, int mtimeSec) + String remotePath, int mode, int mtimeSec) throws IOException { + Objects.requireNonNull(src); + Objects.requireNonNull(out); + Objects.requireNonNull(in); + Objects.requireNonNull(remotePath); + if (remotePath.isEmpty() || remotePath.indexOf(',') >= 0 + || remotePath.indexOf('\0') >= 0) { + throw new IllegalArgumentException("invalid sync path"); + } + if (mode < 0) throw new IllegalArgumentException("invalid sync mode"); String header = remotePath + "," + mode; byte[] hb = header.getBytes(StandardCharsets.UTF_8); + if (hb.length > MAX_PATH_BYTES) { + throw new IllegalArgumentException("sync path and mode are too long"); + } byte[] tag = new byte[8]; putTag(tag, 0, "SEND"); @@ -46,7 +60,8 @@ public final class Sync { putTag(dataHdr, 0, "DATA"); long total = 0; int n; - while ((n = src.read(chunk)) > 0) { + while ((n = src.read(chunk)) >= 0) { + if (n == 0) throw new IOException("sync source made no progress"); Wire.writeLe32(dataHdr, 4, n); out.write(dataHdr); out.write(chunk, 0, n); @@ -63,16 +78,43 @@ public final class Sync { Wire.readFully(in, resp); String code = new String(resp, 0, 4, StandardCharsets.US_ASCII); int len = Wire.readLe32(resp, 4); - if ("OKAY".equals(code)) return total; + if ("OKAY".equals(code)) { + if (len != 0) throw new IOException("sync OKAY length is not zero: " + len); + return total; + } + if (!"FAIL".equals(code)) { + throw new IOException("unknown sync response: " + safeCode(code)); + } - byte[] msg = new byte[Math.min(Math.max(0, len), MAX_FAIL_MSG)]; + if (len < 0 || len > MAX_FAIL_MSG) { + throw new IOException("sync " + code + " response length out of range: " + len); + } + byte[] msg = new byte[len]; if (msg.length > 0) Wire.readFully(in, msg); - throw new IOException("sync " + code + ": " - + new String(msg, StandardCharsets.UTF_8)); + throw new IOException("sync FAIL: " + safeMessage(Wire.decodeUtf8(msg))); } private static void putTag(byte[] dst, int off, String tag) { byte[] b = tag.getBytes(StandardCharsets.US_ASCII); System.arraycopy(b, 0, dst, off, 4); } + + private static String safeCode(String code) { + StringBuilder out = new StringBuilder(code.length()); + for (int i = 0; i < code.length(); i++) { + char c = code.charAt(i); + out.append(c >= 0x20 && c <= 0x7e ? c : '?'); + } + return out.toString(); + } + + private static String safeMessage(String message) { + StringBuilder out = new StringBuilder(message.length()); + for (int i = 0; i < message.length(); i++) { + char c = message.charAt(i); + int type = Character.getType(c); + out.append(Character.isISOControl(c) || type == Character.FORMAT ? '?' : c); + } + return out.toString(); + } } |