# Risk map — Genymobile/scrcpy

> Canonical page: https://codevetter.com/unpack/scrcpy/risk-map

# Risk map — Genymobile/scrcpy

Scanned commit 19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac (2026-09-05). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

The riskiest areas are: (1) shell-injection-adjacent code — server params are interpolated into an 'adb shell ... app_process' command line and only mitigated by a denylist VALIDATE_STRING, with an explicit comment that args are not properly escaped on Windows; (2) heavy reflection into private Android APIs (Workarounds, FakeContext, wrappers) which can break per Android release; (3) fragile lifecycle ordering — a large goto-cleanup chain in scrcpy() and static buffers/structs; (4) privileged-environment handling (root uid drop, killing the adb server, writing to /data/local/tmp and /sdcard); and (5) untested protocol duality where the same wire format is hand-maintained in C and Java.
- Server parameter values are appended verbatim to an adb shell command line; validate_string() only rejects a denylist of shell metacharacters (' ;'"*$?&`#\|<>[]{}()!~\r\n') and the comment admits proper escaping does not exist on Windows (sys/win/process.c). — [app/src/server.c#L193-206](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/server.c#L193-L206)
- The command array in execute_server() is a fixed 'const char *cmd[128]' filled by ADD_PARAM macros — adding server options without bound checks could overflow it (only ~60 slots used today). — [app/src/server.c#L216-262](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/server.c#L216-L262)
- SCRCPY_SERVER_PATH env var overrides the pushed server jar; a malicious local env could push an arbitrary jar executed as shell (uid 2000) on the device. — [app/src/server.c#L26-54](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/server.c#L26-L54)
- If run as root, the server silently drops to uid 2000 via Os.setuid — a comment notes copy-paste breaks as root; failure is only a warning (best-effort privilege drop). — [server/src/main/java/com/genymobile/scrcpy/Server.java#L279-290](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/server/src/main/java/com/genymobile/scrcpy/Server.java#L279-L290)
- Workarounds uses reflection over private/blocked Android APIs (@SuppressLint PrivateApi/BlockedPrivateApi/SoonBlockedPrivateApi) e.g. ActivityThread internals and fake Context — each new Android version can silently break capture/audio. — [server/src/main/java/com/genymobile/scrcpy/Workarounds.java#L1-30](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/server/src/main/java/com/genymobile/scrcpy/Workarounds.java#L1-L30) [server/src/main/java/com/genymobile/scrcpy/FakeContext.java](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/server/src/main/java/com/genymobile/scrcpy/FakeContext.java)
- prepareMainLooper() uses reflection on the private static field Looper.sMainLooper — an AOSP-internal implementation detail that could change. (inferred) — [server/src/main/java/com/genymobile/scrcpy/Server.java#L197-210](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/server/src/main/java/com/genymobile/scrcpy/Server.java#L197-L210)
- --kill-adb-on-close terminates the entire adb server (sc_adb_kill_server), which has a wide blast radius: it disrupts every other adb session/tool on the machine. — [app/src/server.c#L955-962](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/server.c#L955-L962)
- The control queue silently drops input events beyond 60 pending messages except UHID_CREATE/UHID_DESTROY; under socket backpressure users lose keystrokes with only a verbose-level trace. — [app/src/controller.c#L7-8](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/controller.c#L7-L8) [app/src/controller.c#L95-126](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/controller.c#L95-L126) [app/src/control_msg.c#L358-366](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/control_msg.c#L358-L366)
- scrcpy() uses a single static 'struct scrcpy' (memset to 42 in debug to catch uninit) and a long goto-based cleanup ladder — adding init steps without updating the teardown ordering risks use-after-free, e.g. screen must be destroyed only after video demuxer join. — [app/src/scrcpy.c#L322-360](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/scrcpy.c#L322-L360) [app/src/scrcpy.c#L1002-1008](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/scrcpy.c#L1002-L1008)
- The wire protocol is implemented twice by hand (control_msg.c/h + demuxer.c vs ControlMessage*.java + Streamer.java); nothing enforces parity except matching constants like SC_CONTROL_MSG_MAX_SIZE == MESSAGE_MAX_SIZE == 256k. — [app/src/control_msg.h#L15-21](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/control_msg.h#L15-L21) [server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java#L14-17](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java#L14-L17)
- Device reads trust the peer: device_read_info() force-NUL-terminates the 64-byte name ('in case the client sends garbage') but packet sizes drive av_new_packet allocations of up to 4GB-1 declared bytes before any cap check. — [app/src/server.c#L596-610](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/server.c#L596-L610) [app/src/demuxer.c#L141-160](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/demuxer.c#L141-L160)
- File push writes to device paths: server jar to /data/local/tmp (world-writable staging area) and user files to /sdcard/Download/ by default; SCAN_FILE messages point the device media scanner at pushed paths. — [app/src/server.c#L21](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/server.c#L21) [app/src/file_pusher.c#L12](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/file_pusher.c#L12) [app/src/file_pusher.c#L115-131](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/app/src/file_pusher.c#L115-L131)
- Version skew is hard-failed: server refuses to run if client version != BuildConfig.VERSION_NAME, so mismatched server jars fail loudly rather than silently misbehaving. — [server/src/main/java/com/genymobile/scrcpy/Options.java#L328-332](https://github.com/Genymobile/scrcpy/blob/19c1261d2e2cbf2b5e6a71a8b64cc1dd3ede06ac/server/src/main/java/com/genymobile/scrcpy/Options.java#L328-L332)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
