blob: 4c27416e4802d510c8501ec262ab42a02c519156 [file] [log] [blame]
Andy Hung1fe82772015-08-31 19:00:47 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "AudioEffectBinderTest-JNI"
18
19#include <jni.h>
20#include <media/AudioEffect.h>
21#include <media/IEffect.h>
22
23using namespace android;
24
25/*
26 * Native methods used by
27 * cts/tests/tests/security/src/android/security/cts/AudioEffectBinderTest.java
28 */
29
30struct EffectClient : public BnEffectClient {
31 EffectClient() { }
32 virtual void controlStatusChanged(bool controlGranted __unused) { }
33 virtual void enableStatusChanged(bool enabled __unused) { }
34 virtual void commandExecuted(uint32_t cmdCode __unused,
35 uint32_t cmdSize __unused,
36 void *pCmdData __unused,
37 uint32_t replySize __unused,
38 void *pReplyData __unused) { }
39};
40
41struct DeathRecipient : public IBinder::DeathRecipient {
42 DeathRecipient() : mDied(false) { }
43 virtual void binderDied(const wp<IBinder>& who __unused) { mDied = true; }
44 bool died() const { return mDied; }
45 bool mDied;
46};
47
48static bool isIEffectCommandSecure(IEffect *effect)
49{
50 // some magic constants here
51 const int COMMAND_SIZE = 1024 + 12; // different than reply size to get different heap frag
52 char cmdData[COMMAND_SIZE];
53 memset(cmdData, 0xde, sizeof(cmdData));
54
55 const int REPLY_DATA_SIZE = 256;
56 char replyData[REPLY_DATA_SIZE];
57 bool secure = true;
58 for (int k = 0; k < 10; ++k) {
59 Parcel data;
60 data.writeInterfaceToken(effect->getInterfaceDescriptor());
61 data.writeInt32(0); // 0 is EFFECT_CMD_INIT
62 data.writeInt32(sizeof(cmdData));
63 data.write(cmdData, sizeof(cmdData));
64 data.writeInt32(sizeof(replyData));
65
66 Parcel reply;
67 status_t status = effect->asBinder(effect)->transact(3, data, &reply); // 3 is COMMAND
68 ALOGV("transact status: %d", status);
69 if (status != NO_ERROR) {
70 ALOGW("invalid transaction status %d", status);
71 continue;
72 }
73
74 ALOGV("reply data avail %zu", reply.dataAvail());
75 status = reply.readInt32();
76 ALOGV("reply status %d", status);
77 if (status == NO_ERROR) {
78 continue;
79 }
80
81 int size = reply.readInt32();
82 ALOGV("reply size %d", size);
83 if (size != sizeof(replyData)) { // we expect 0 or full reply data if command failed
84 ALOGW_IF(size != 0, "invalid reply size: %d", size);
85 continue;
86 }
87
88 // Note that if reply.read() returns success, it should completely fill replyData.
89 status = reply.read(replyData, sizeof(replyData));
90 if (status != NO_ERROR) {
91 ALOGW("invalid reply read - ignoring");
92 continue;
93 }
94 unsigned int *out = (unsigned int *)replyData;
95 for (size_t index = 0; index < sizeof(replyData) / sizeof(*out); ++index) {
96 if (out[index] != 0) {
97 secure = false;
98 ALOGI("leaked data = %#08x", out[index]);
99 }
100 }
101 }
102 ALOGI("secure: %s", secure ? "YES" : "NO");
103 return secure;
104}
105
106static jboolean android_security_cts_AudioEffect_test_isCommandSecure()
107{
108 const sp<IAudioFlinger> &audioFlinger = AudioSystem::get_audio_flinger();
109 if (audioFlinger.get() == NULL) {
110 ALOGE("could not get audioflinger");
111 return JNI_FALSE;
112 }
113
114 static const effect_uuid_t EFFECT_UIID_EQUALIZER = // type
115 { 0x0bed4300, 0xddd6, 0x11db, 0x8f34, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b }};
116 sp<EffectClient> effectClient(new EffectClient());
117 effect_descriptor_t descriptor;
118 memset(&descriptor, 0, sizeof(descriptor));
119 descriptor.type = EFFECT_UIID_EQUALIZER;
120 descriptor.uuid = *EFFECT_UUID_NULL;
121 const int32_t priority = 0;
122 const int sessionId = AUDIO_SESSION_OUTPUT_MIX;
123 const audio_io_handle_t io = AUDIO_IO_HANDLE_NONE;
124 const String16 opPackageName("Exploitable");
125 status_t status;
126 int32_t id;
127 int enabled;
128 sp<IEffect> effect = audioFlinger->createEffect(&descriptor, effectClient,
129 priority, io, sessionId, opPackageName, &status, &id, &enabled);
130 if (effect.get() == NULL || status != NO_ERROR) {
131 ALOGW("could not create effect");
132 return JNI_TRUE;
133 }
134
135 sp<DeathRecipient> deathRecipient(new DeathRecipient());
136 IInterface::asBinder(effect)->linkToDeath(deathRecipient);
137
138 // check exploit
139 if (!isIEffectCommandSecure(effect.get())) {
140 ALOGE("not secure!");
141 return JNI_FALSE;
142 }
143
144 sleep(1); // wait to check death
145 if (deathRecipient->died()) {
146 ALOGE("effect binder died");
147 return JNI_FALSE;
148 }
149 return JNI_TRUE;
150}
151
152int register_android_security_cts_AudioEffectBinderTest(JNIEnv *env)
153{
154 static JNINativeMethod methods[] = {
155 { "native_test_isCommandSecure", "()Z",
156 (void *) android_security_cts_AudioEffect_test_isCommandSecure },
157 };
158
159 jclass clazz = env->FindClass("android/security/cts/AudioEffectBinderTest");
160 return env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0]));
161}