* Copyright (c) 2025-2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <dirent.h>
#include "securec.h"
#include "appspawn_utils.h"
#include "appspawn_msg.h"
#include "appspawn_hook.h"
#include "appspawn_manager.h"
#define ISOLATE_PATH_BUFF_SIZE 512
#define ISOLATE_PATH_NUM 3
#define ISOLATE_PATH_CAPACITY 16
#define HM_DEC_IOCTL_BASE 's'
#define HM_ADD_PATH_MARK 11
#define HM_ADD_ISOLATE_DIR 16
#define ADD_ISOLATE_DIR_CMD _IOWR(HM_DEC_IOCTL_BASE, HM_ADD_ISOLATE_DIR, IsolateDirInfo)
#define ADD_PATH_MARK_CMD _IOWR(HM_DEC_IOCTL_BASE, HM_ADD_PATH_MARK, MarkPathInfo)
#define SEC_SANDBOX_PATH_TYPE (1 << 3)
#define MARK_PATH_INFO_RESERVED_SIZE 7
#define MARK_ENABLE_RECURSIVE 1
typedef struct {
char *path;
int len;
} IsolateDir;
typedef struct {
IsolateDir dirs[ISOLATE_PATH_CAPACITY];
int dirNum;
} IsolateDirInfo;
typedef struct {
const char *prefix;
const char *suffix;
} IsolateDirAffix;
typedef struct MarkPathInfo {
char *path;
uint32_t flags;
uint32_t recursive;
uint32_t reserved[MARK_PATH_INFO_RESERVED_SIZE];
} MarkPathInfo;
static const IsolateDirAffix g_dirAffix[ISOLATE_PATH_NUM] = {{"/data/app/el2", "base"},
{"/storage/media", "local/files/Docs"},
{"/data/app/el1", "base"}};
static void FreeIsolatePath(IsolateDirInfo *isolateDirInfo)
{
for (int i = 0; i < isolateDirInfo->dirNum; ++i) {
free(isolateDirInfo->dirs[i].path);
isolateDirInfo->dirs[i].path = NULL;
}
isolateDirInfo->dirNum = 0;
}
* To avoid memory leaks,
* it needs to be called in pairs with FreeIsolatePath.
*/
static int MallocIsolatePath(IsolateDirInfo *isolateDirInfo)
{
isolateDirInfo->dirNum = 0;
for (int i = 0; i < ISOLATE_PATH_NUM; ++i) {
char *path = (char *)calloc(1, ISOLATE_PATH_BUFF_SIZE);
if (path == NULL) {
APPSPAWN_LOGE("Failed to calloc buffer for path");
FreeIsolatePath(isolateDirInfo);
return APPSPAWN_SYSTEM_ERROR;
}
isolateDirInfo->dirs[i].path = path;
++isolateDirInfo->dirNum;
}
return 0;
}
static int GetNormalUser(const AppSpawningCtx *property, uid_t *user)
{
const char *bundleName = GetBundleName(property);
APPSPAWN_CHECK(bundleName != NULL, return APPSPAWN_ARG_INVALID, "Can not get bundle name");
AppSpawnMsgDacInfo *dacInfo = (AppSpawnMsgDacInfo *)GetAppProperty(property, TLV_DAC_INFO);
APPSPAWN_CHECK(dacInfo != NULL, return APPSPAWN_TLV_NONE, "No tlv %{public}d in msg %{public}s",
TLV_DOMAIN_INFO, bundleName);
APPSPAWN_CHECK(dacInfo->uid / UID_BASE != 0, return APPSPAWN_ARG_INVALID, "Not normal user");
*user = dacInfo->uid / UID_BASE;
return APPSPAWN_OK;
}
static int FillIsolateInfo(const AppSpawningCtx *property, IsolateDirInfo *isolateDirInfo)
{
uid_t user = 0;
int ret = GetNormalUser(property, &user);
if (ret != 0) {
APPSPAWN_LOGE("Get normal User failed, ret %{public}d", ret);
return ret;
}
for (int dirIdx = 0; dirIdx < ISOLATE_PATH_NUM; dirIdx++) {
ret = snprintf_s(isolateDirInfo->dirs[dirIdx].path, ISOLATE_PATH_BUFF_SIZE,
ISOLATE_PATH_BUFF_SIZE - 1, "%s/%u/%s",
g_dirAffix[dirIdx].prefix, user, g_dirAffix[dirIdx].suffix);
if (ret < 0) {
APPSPAWN_LOGE("snprintf_s dir path failed, dirIdx %{public}d, ret %{public}d", dirIdx, ret);
isolateDirInfo->dirs[dirIdx].len = 0;
continue;
}
isolateDirInfo->dirs[dirIdx].len = ret;
}
return 0;
}
APPSPAWN_STATIC int SetIsolateDir(const AppSpawningCtx *property)
{
int ret = 0;
IsolateDirInfo isolateDirInfo = {0};
ret = MallocIsolatePath(&isolateDirInfo);
if (ret != 0) {
APPSPAWN_LOGE("Failed to malloc isolate dir");
return APPSPAWN_SYSTEM_ERROR;
}
ret = FillIsolateInfo(property, &isolateDirInfo);
if (ret != 0) {
APPSPAWN_LOGE("Failed to fill isolate dir info");
FreeIsolatePath(&isolateDirInfo);
return APPSPAWN_SYSTEM_ERROR;
}
int fd = open("/dev/dec", O_RDWR);
if (fd < 0) {
APPSPAWN_LOGE("Open dec file fail, errno %{public}d", errno);
FreeIsolatePath(&isolateDirInfo);
return APPSPAWN_SYSTEM_ERROR;
}
ret = ioctl(fd, ADD_ISOLATE_DIR_CMD, &isolateDirInfo);
if (ret != 0) {
APPSPAWN_LOGE("ioctl ADD_ISOLATE_DIR_CMD fail, errno %{public}d", errno);
FreeIsolatePath(&isolateDirInfo);
close(fd);
return APPSPAWN_SYSTEM_ERROR;
}
APPSPAWN_LOGV("ioctl ADD_ISOLATE_DIR_CMD success");
close(fd);
FreeIsolatePath(&isolateDirInfo);
return APPSPAWN_OK;
}
static bool IsIsolateDirNeeded(const AppSpawnMgr *content)
{
* NWebSpawn is used for spawn render process and gpu process,
* so it is no need to set isolate dir.
*/
if (IsNWebSpawnMode(content)) {
return false;
}
return true;
}
static int SpawnSetIsolateDir(AppSpawnMgr *content, AppSpawningCtx *property)
{
if (IsIsolateDirNeeded(content)) {
int ret = SetIsolateDir(property);
APPSPAWN_CHECK_ONLY_LOG(ret == 0, "Failed to set isolate dir, ret %{public}d", ret);
}
return APPSPAWN_OK;
}
static int MarkSandboxMounts(AppSpawnMgr *content, AppSpawningCtx *property)
{
if (IsNWebSpawnMode(content) || GetAppSpawnMsgType(property) == MSG_SPAWN_NATIVE_PROCESS) {
return 0;
}
const char *decFilename = "/dev/dec";
int fd = open(decFilename, O_RDWR);
APPSPAWN_CHECK(fd >= 0, return 0, "Open dec file failed errno %{public}d", errno);
MarkPathInfo pathInfo = { "/", SEC_SANDBOX_PATH_TYPE, MARK_ENABLE_RECURSIVE };
if (ioctl(fd, ADD_PATH_MARK_CMD, &pathInfo) < 0) {
APPSPAWN_LOGE("Set %{public}s 0x%{public}x mark failed errno %{public}d", pathInfo.path, pathInfo.flags, errno);
} else {
APPSPAWN_LOGV("Set %{public}s 0x%{public}x mark success", pathInfo.path, pathInfo.flags);
}
close(fd);
return 0;
}
MODULE_CONSTRUCTOR(void)
{
AddAppSpawnHook(STAGE_CHILD_PRE_COLDBOOT, HOOK_PRIO_HIGHEST, SpawnSetIsolateDir);
AddAppSpawnHook(STAGE_CHILD_EXECUTE, HOOK_PRIO_SANDBOX_MARK_PATH, MarkSandboxMounts);
}