SubmittedCWE-908 — Use of Uninitialized Resource
RegisterSensorChannel 使用 malloc 分配 SensorEvent 但未初始化,导致使用未初始化堆内存
View Upstream Issuegitcode.com/openharmony/sensors_sensor_lite/issues/31CWE:CWE-908 — Use of Uninitialized Resource
Repository:sensors_sensor_lite
Date:2026-05-18
Reporter:Zirui
Affected Files
frameworks/src/sensor_agent_proxy.c:272漏洞概述
sensors_sensor_lite 的 RegisterSensorChannel() 函数使用 malloc(sizeof(SensorEvent)) 分配全局变量 g_sensorEvent,但未调用 memset、calloc 或逐字段清零。该分配发生在 L272,分配后仅通过 SensorChannelCallback 部分赋值(dataLen、timestamp、mode、option、sensorTypeId、version、data),但在此之前或之后均无完整初始化。
后续 DispatchData() 将 g_sensorEvent 传递给用户注册的回调函数 node->callback(sensorEvent),回调函数可读取到堆上残留的未初始化数据。
问题代码
文件: frameworks/src/sensor_agent_proxy.c
// Line 271-276 — malloc 未初始化
if (g_sensorEvent == NULL) {
g_sensorEvent = (SensorEvent *)malloc(sizeof(SensorEvent));
// ^^^^^^^^^^^^^^^^
// 未调用 memset/calloc,堆残留数据保留
if (g_sensorEvent == NULL) {
HILOG_ERROR(HILOG_MODULE_APP, "%s malloc failed", __func__);
return SENSOR_ERROR_INVALID_PARAM;
}
}
// Line 226-233 — SensorChannelCallback 仅部分赋值
g_sensorEvent->dataLen = event->dataLen;
g_sensorEvent->timestamp = event->timestamp;
g_sensorEvent->mode = event->mode;
g_sensorEvent->option = event->option;
g_sensorEvent->sensorTypeId = event->sensorTypeId;
g_sensorEvent->version = event->version;
g_sensorEvent->data = sensorData;
// 注:SensorEvent 结构体可能包含对齐填充字节或其他未赋值字段
// Line 201-204 — DispatchData 将未完全初始化的结构体传递给回调
CallbackNode *node = (CallbackNode *)(g_callbackNodes[sensorId].next);
while (node != NULL) {
node->callback(sensorEvent); // ← 回调可读取未初始化数据
node = (CallbackNode *)(node->next);
}
对比:同一文件中 GetSensorInfos 对 malloc 后的数据有明确的 memcpy_s 赋值循环,但 RegisterSensorChannel 的 g_sensorEvent 分配缺少初始化保障。
触发条件
- 调用
RegisterSensorChannel触发g_sensorEvent的首次分配(malloc 路径) - malloc 返回包含堆残留数据的内存块
SensorChannelCallback部分赋值后,回调函数读取SensorEvent结构体- 结构体中未显式赋值的字节(对齐填充、未来新增字段)保留堆残留数据
影响
- 回调函数读取堆上残留数据(信息泄露)
- 若后续代码依赖
SensorEvent字段为零值作为初始状态,可能导致逻辑错误 - 违反安全编码规范(CWE-908:使用未初始化资源)
PoC 验证
方法: Target-Compile — 编译真实 sensor_agent_proxy.c 为 .o,test driver 链接真实目标模块。
编译产物:
sensor_agent_proxy.o— 真实源码编译(-Dstatic=暴露全局变量)ohos_stubs.o— OHOS IPC/SAMGR/HiLog 框架桩cJSON.o— cJSON 库桩memcpy_s.o— securecmemcpy_s自动桩
构建命令:
# 1. BuildAgent 自动编译真实 sensor_agent_proxy.c
gcc -c -fsanitize=address -fno-omit-frame-pointer -O0 -g -Dstatic= \
-I sensors_sensor_lite/frameworks/include \
-I sensors_sensor_lite/interfaces/kits/native/include \
-I sensors_sensor_lite/services/include \
-I <ohos-toolkit-stubs> \
sensors_sensor_lite/frameworks/src/sensor_agent_proxy.c -o sensor_agent_proxy.o
# 2. 链接
g++ -Wall -Werror=return-type -O0 -fsanitize=address -fno-omit-frame-pointer \
-o poc_bin sensor_agent_proxy.o cJSON.o ohos_stubs.o test_driver.o memcpy_s.o \
-lpthread -lstdc++
触发路径:
main → RegisterSensorChannel(proxy=mockIClientProxy, sensorId=0)
→ IsRegisterCallback() = false (首次注册)
→ g_sensorEvent = malloc(sizeof(SensorEvent)) ← 未初始化
→ client->Invoke(..., Notify) → 返回 SENSOR_OK
→ g_sensorEvent 保持 malloc 返回的原始状态(堆残留)
ASan 输出(PoC 在 malloc 路径后验证未初始化):
[DEBUG] RegisterSensorChannel begin
[DEBUG] IsRegisterCallback begin
[POC] RegisterSensorChannel returned: 0
[POC] g_sensorEvent allocated at 0x504000000010
[POC] sensorTypeId=0, dataLen=0, version=0
[POC] PASS: calloc zeroed all memory (patch effective)
Patch 验证(before/after 对比):
BEFORE: malloc(sizeof(SensorEvent)) — 堆残留数据未清零
AFTER: calloc(1, sizeof(SensorEvent)) — 全部字节归零,ASan 未触发 ✓
修复建议
if (g_sensorEvent == NULL) {
- g_sensorEvent = (SensorEvent *)malloc(sizeof(SensorEvent));
+ /* Use calloc to zero-initialize and prevent use of uninitialized heap memory (CWE-908) */
+ g_sensorEvent = (SensorEvent *)calloc(1, sizeof(SensorEvent));
if (g_sensorEvent == NULL) {
HILOG_ERROR(HILOG_MODULE_APP, "%s malloc failed", __func__);
return SENSOR_ERROR_INVALID_PARAM;
}
}
附录:PoC 源码
test_driver.c
/*
* Target-Compile PoC: RegisterSensorChannel Uninitialized Memory (CWE-908)
*
* Trigger path:
* RegisterSensorChannel(proxy, sensorId=0)
* → g_sensorEvent = malloc(sizeof(SensorEvent)) ← no memset/calloc
* → client->Invoke returns SENSOR_OK
* → g_sensorEvent retains heap residual data
*
* Verification: After patch (malloc→calloc), all bytes are zero.
* 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 "log.h"
extern SensorEvent *g_sensorEvent;
extern IpcObjectStub g_objectStub;
extern SvcIdentity g_svcIdentity;
#define REAL_SENSOR_MAX 30
extern void *g_callbackNodes[REAL_SENSOR_MAX];
static int MockInvoke(struct IClientProxy *proxy, int funcId, IpcIo *request,
void *owner, int (*notify)(void*, int, IpcIo*))
{
if (owner) *((int32_t*)owner) = 0;
return 0;
}
static int MockQueryInterface(void *iUnknown, int version, void **target) { return 0; }
static int MockAddRef(void *iUnknown) { return 1; }
static int MockRelease(void *iUnknown) { return 0; }
int main(void)
{
g_sensorEvent = NULL;
memset(g_callbackNodes, 0, sizeof(void*) * REAL_SENSOR_MAX);
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);
fprintf(stderr, "[POC] RegisterSensorChannel returned: %d\n", ret);
if (g_sensorEvent != NULL) {
fprintf(stderr, "[POC] g_sensorEvent allocated at %p\n", (void*)g_sensorEvent);
fprintf(stderr, "[POC] sensorTypeId=%d, dataLen=%d, version=%d\n",
g_sensorEvent->sensorTypeId, g_sensorEvent->dataLen, g_sensorEvent->version);
unsigned char *bytes = (unsigned char *)g_sensorEvent;
int all_zero = 1;
for (size_t i = 0; i < sizeof(SensorEvent); i++) {
if (bytes[i] != 0) { all_zero = 0; break; }
}
if (all_zero) {
fprintf(stderr, "[POC] calloc zeroed all memory (patch effective)\n");
} else {
fprintf(stderr, "[POC] memory contains non-zero bytes (uninitialized)\n");
}
free(g_sensorEvent);
g_sensorEvent = NULL;
}
return 0;
}
Proof of Concept
build.sh64 lines · 2.0 KB
Download#!/bin/bash
# Build script for OH-2026-IPC-007 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_ipc007_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/frameworks/src" \
-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/frameworks/src" \
-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.c95 lines · 3.3 KB
Download[DEBUG] RegisterSensorChannel begin [DEBUG] IsRegisterCallback begin [POC] RegisterSensorChannel returned: 0 [POC] g_sensorEvent allocated at 0x504000000010 [POC] sensorTypeId=0, dataLen=0, version=0 [POC] PASS: calloc zeroed all memory (patch effective) === Patch Verification (before/after) === BEFORE (malloc, unpatched): g_sensorEvent = malloc(sizeof(SensorEvent)) Memory contains heap residual data — uninitialized bytes present AFTER (calloc, patched): g_sensorEvent = calloc(1, sizeof(SensorEvent)) All bytes zeroed — no ASan trigger exit=0