已合并
DEC策略下发重构,单配置超限截断为前32条 #2938
DEC策略下发重构,单配置超限截断为前32条 #2938
已合并
soulrequiem创建于 12 天前
8 个文件变更+1545-111
@@ -1518,10 +1518,12 @@ int32_t SandboxCore::SetDecPolicyWithPermission(const AppSpawningCtx *appPropert
1518 APPSPAWN_CHECK(tokenInfo != nullptr, return APPSPAWN_MSG_INVALID, "Get token id failed.");1518 APPSPAWN_CHECK(tokenInfo != nullptr, return APPSPAWN_MSG_INVALID, "Get token id failed.");
1519 1519 
1520 DecPolicyInfo decPolicyInfo = {0};1520 DecPolicyInfo decPolicyInfo = {0};
1521- decPolicyInfo.pathNum = mountConfig.decPaths.size();1521+ uint32_t totalPaths = mountConfig.decPaths.size();
1522- // Kernel supports at most KERNEL_BATCH_SIZE DEC policies per request1522+ decPolicyInfo.pathNum = (totalPaths > MAX_CONFIG_POLICY_NUM) ? MAX_CONFIG_POLICY_NUM : totalPaths;
1523- APPSPAWN_CHECK(decPolicyInfo.pathNum <= KERNEL_BATCH_SIZE, return APPSPAWN_SANDBOX_DEC_OUT_BOUND,1523+ if (totalPaths > MAX_CONFIG_POLICY_NUM) {
1524- "dec policy out of bound %{public}d", decPolicyInfo.pathNum);1524+ APPSPAWN_LOGW("dec policy %{public}u exceeds max %{public}d, truncating to first %{public}d",
1525+ totalPaths, MAX_CONFIG_POLICY_NUM, MAX_CONFIG_POLICY_NUM);
1526+ }
W
Wwangfenging12 天前

🤖 AI 代码检视意见 | ✨ Quality | ✅ Low | 行号区间: L1524-L1526

问题描述: 日志"dec policy %{public}u exceeds max %{public}d, truncating to first %{public}d"中,totalPaths为uint32_t使用%u正确,但MAX_CONFIG_POLICY_NUM为宏定义展开后类型不确定,使用%d假设为有符号int。若MAX_CONFIG_POLICY_NUM被定义为无符号字面量(如32u),传入%d属于未定义行为。建议统一使用%u并显式转换。

💡 查看修复建议(点击展开)

修改建议:将MAX_CONFIG_POLICY_NUM显式转换为uint32_t,并统一使用%u格式化符,避免类型不匹配。

APPSPAWN_LOGW("dec policy %{public}u exceeds max %{public}u, truncating to first %{public}u",
totalPaths, (uint32_t)MAX_CONFIG_POLICY_NUM, (uint32_t)MAX_CONFIG_POLICY_NUM);
}
likedislike
1525 int ret = 0;1527 int ret = 0;
1526 for (uint32_t i = 0; i < decPolicyInfo.pathNum; i++) {1528 for (uint32_t i = 0; i < decPolicyInfo.pathNum; i++) {
1527 PathInfo pathInfo = {0};1529 PathInfo pathInfo = {0};
@@ -1557,9 +1559,12 @@ int32_t SandboxCore::SetDecReadOnlyPolicyWithPermission(
1557 reinterpret_cast<AppSpawnMsgAccessToken *>(GetAppProperty(appProperty, TLV_ACCESS_TOKEN_INFO));1559 reinterpret_cast<AppSpawnMsgAccessToken *>(GetAppProperty(appProperty, TLV_ACCESS_TOKEN_INFO));
1558 APPSPAWN_CHECK(tokenInfo != nullptr, return APPSPAWN_MSG_INVALID, "Get token id failed.");1560 APPSPAWN_CHECK(tokenInfo != nullptr, return APPSPAWN_MSG_INVALID, "Get token id failed.");
1559 DecPolicyInfo decPolicyInfo = {0};1561 DecPolicyInfo decPolicyInfo = {0};
1560- decPolicyInfo.pathNum = mountConfig.decReadOnlyPaths.size();1562+ uint32_t totalReadOnlyPaths = mountConfig.decReadOnlyPaths.size();
1561- APPSPAWN_CHECK(decPolicyInfo.pathNum <= KERNEL_BATCH_SIZE, return APPSPAWN_SANDBOX_DEC_OUT_BOUND,1563+ decPolicyInfo.pathNum = (totalReadOnlyPaths > MAX_CONFIG_POLICY_NUM) ? MAX_CONFIG_POLICY_NUM : totalReadOnlyPaths;
1562- "dec read-only policy out of bound %{public}d", decPolicyInfo.pathNum);1564+ if (totalReadOnlyPaths > MAX_CONFIG_POLICY_NUM) {
1565+ APPSPAWN_LOGW("dec read-only policy %{public}u exceeds max %{public}d, truncating to first %{public}d",
1566+ totalReadOnlyPaths, MAX_CONFIG_POLICY_NUM, MAX_CONFIG_POLICY_NUM);
1567+ }
1563 int ret = 0;1568 int ret = 0;
1564 for (uint32_t i = 0; i < decPolicyInfo.pathNum; i++) {1569 for (uint32_t i = 0; i < decPolicyInfo.pathNum; i++) {
1565 PathInfo pathInfo = {0};1570 PathInfo pathInfo = {0};
@@ -1607,7 +1612,7 @@ void SandboxCore::SetDecDenyWithDir(const AppSpawningCtx *appProperty)
1607 if (CheckAppPermissionFlagSet(appProperty, static_cast<uint32_t>(index))) {1612 if (CheckAppPermissionFlagSet(appProperty, static_cast<uint32_t>(index))) {
1608 continue;1613 continue;
1609 }1614 }
1610- APPSPAWN_CHECK(j < KERNEL_BATCH_SIZE, return, "dec policy out of bound currentIndex %{public}d", j);1615+ APPSPAWN_CHECK(j < MAX_CONFIG_POLICY_NUM, return, "dec policy out of bound currentIndex %{public}d", j);
1611 PathInfo pathInfo = {0};1616 PathInfo pathInfo = {0};
1612 pathInfo.path = const_cast<char *>(DEC_DENY_PATH_MAP[i].decPath);1617 pathInfo.path = const_cast<char *>(DEC_DENY_PATH_MAP[i].decPath);
1613 pathInfo.pathLen = static_cast<uint32_t>(strlen(pathInfo.path));1618 pathInfo.pathLen = static_cast<uint32_t>(strlen(pathInfo.path));
@@ -25,6 +25,24 @@
25#include "dec_config.h"25#include "dec_config.h"
26#include "securec.h"26#include "securec.h"
27 27 
28+// Kernel ABI batch type for DEC ioctl. path capacity is fixed to KERNEL_BATCH_SIZE.
29+// Used by all DEC batch ioctl commands to keep _IOWR/_IOW size consistent with the kernel.
30+typedef struct IoctlDecPolicyBatch {
31+ uint64_t tokenId;
32+ uint64_t timestamp;
33+ PathInfo path[KERNEL_BATCH_SIZE];
34+ uint32_t pathNum;
35+ int32_t userId;
36+ uint64_t reserved[DEC_POLICY_HEADER_RESERVED];
37+ bool flag;
38+} IoctlDecPolicyBatch;
39+ 
40+// Internal ioctl commands, all based on IoctlDecPolicyBatch (path[KERNEL_BATCH_SIZE]).
41+#define SET_DEC_POLICY_CMD _IOWR(HM_DEC_IOCTL_BASE, HM_SET_POLICY_ID, IoctlDecPolicyBatch)
42+#define CONSTRAINT_DEC_POLICY_CMD _IOW(HM_DEC_IOCTL_BASE, HM_CONSTRAINT_POLICY_ID, IoctlDecPolicyBatch)
43+#define SET_DEC_PREFIX_CMD _IOWR(HM_DEC_IOCTL_BASE, HM_SET_PREFIX_ID, IoctlDecPolicyBatch)
44+#define SET_DEC_IGNORE_CASE_CMD _IOWR(HM_DEC_IOCTL_BASE, HM_SET_DEC_IGNORE_CASE_ID, IoctlDecPolicyBatch)
45+ 
28static const char *g_decConstraintDir[] = {46static const char *g_decConstraintDir[] = {
29 "/storage/Users",47 "/storage/Users",
30 "/storage/External",48 "/storage/External",
@@ -47,28 +65,35 @@ APPSPAWN_STATIC int SetIgnoreCaseDirs(AppSpawnMgr *content)
47 65 
48 uint32_t pathNum = 0;66 uint32_t pathNum = 0;
49 const DecIgnoreCaseInfo *setInfo = GetDecIgnoreCaseList(IsNoShareFsEnable(), &pathNum);67 const DecIgnoreCaseInfo *setInfo = GetDecIgnoreCaseList(IsNoShareFsEnable(), &pathNum);
50- APPSPAWN_CHECK(setInfo != NULL && pathNum > 0, return 0, "invalid dec ignore case list");68+ APPSPAWN_CHECK(setInfo != NULL && pathNum > 0 && pathNum <= MAX_POLICY_NUM, return 0,
69+ "invalid dec ignore case list %{public}u", pathNum);
51 const char *decFilename = "/dev/dec";70 const char *decFilename = "/dev/dec";
52 int fd = open(decFilename, O_RDWR);71 int fd = open(decFilename, O_RDWR);
53 APPSPAWN_CHECK(fd >= 0, return 0, "Open dec file failed errno %{public}d", errno);72 APPSPAWN_CHECK(fd >= 0, return 0, "Open dec file failed errno %{public}d", errno);
54 73 
55- DecPolicyInfo decPolicyInfos = {0};74+ uint32_t batches = (pathNum + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
56- decPolicyInfos.tokenId = 0;75+ for (uint32_t batch = 0; batch < batches; batch++) {
57- decPolicyInfos.pathNum = pathNum;76+ uint32_t start = batch * KERNEL_BATCH_SIZE;
58- decPolicyInfos.flag = 0;77+ uint32_t end = start + KERNEL_BATCH_SIZE;
W
Wwangfenging12 天前

🤖 AI 代码检视意见 | 🛡️ Reliability | ✅ Low | 行号区间: L73-L76

问题描述: 在SetIgnoreCaseDirs、SetDenyConstraintDirs、SetForcedPrefixDirs中均使用(pathNum + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE计算批次数。若pathNum接近UINT32_MAX(例如来自不可信输入),pathNum + KERNEL_BATCH_SIZE - 1可能整数溢出回绕到很小的值,导致batches计算过小。虽然实际调用方pathNum来自数组长度不会很大,但缺乏防御性检查。同理uint32_t start = batch * KERNEL_BATCH_SIZE;也可能溢出。

💡 查看修复建议(点击展开)

修改建议:在计算批次数前增加pathNum上限校验,或者使用安全的除法上取整宏避免溢出风险。

APPSPAWN_CHECK(pathNum <= MAX_POLICY_NUM, return 0, "pathNum too large %{public}u", pathNum);
uint32_t batches = (pathNum + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
for (uint32_t batch = 0; batch < batches; batch++) {
uint32_t start = batch * KERNEL_BATCH_SIZE;
likedislike
59- 78+ APPSPAWN_ONLY_EXPER(end > pathNum, end = pathNum);
60- for (uint32_t i = 0; i < pathNum; i++) {79+ IoctlDecPolicyBatch decPolicyInfos = {0};
61- PathInfo pathInfo = {80+ decPolicyInfos.pathNum = end - start;
62- .path = (char *)setInfo[i].path,81+ for (uint32_t i = start; i < end; i++) {
63- .pathLen = (uint32_t)strlen(setInfo[i].path),82+ PathInfo pathInfo = {
64- .mode = (uint32_t)setInfo[i].mode,83+ .path = (char *)setInfo[i].path,
65- .flag = false84+ .pathLen = (uint32_t)strlen(setInfo[i].path),
66- };85+ .mode = (uint32_t)setInfo[i].mode,
67- APPSPAWN_LOGV("set ignore case dec policy %{public}s %{public}d", setInfo[i].path, setInfo[i].mode);86+ .flag = false
68- decPolicyInfos.path[i] = pathInfo;87+ };
W
Wwangfenging12 天前

🤖 AI 代码检视意见 | 🔒 Security | ✅ Low | 行号区间: L84-L88

问题描述: SetIgnoreCaseDirs、SetDenyConstraintDirs、SetForcedPrefixDirs函数中均存在path = (char *)setInfo[i].path(char *)g_decConstraintDir[i](char *)g_decForcedPrefix[i]这样的const cast。这些字符串源是只读数据段中的常量字符串,虽然ioctl通常只读取而不写入path字段,但去掉const限定后如果内核或中间处理逻辑意外修改该缓冲区,会导致只读段写入引发段错误。此外const cast属于代码异味,应避免。

💡 查看修复建议(点击展开)

修改建议:PathInfo.path字段类型保持为char*,但在赋值时显式说明这是只读借用,或者使用局部缓冲区拷贝,避免对只读数据去const。

PathInfo pathInfo = {
.path = (char *)setInfo[i].path,  /* ioctl only reads, no write to path */
.pathLen = (uint32_t)strlen(setInfo[i].path),
.mode = (uint32_t)setInfo[i].mode,
.flag = false
likedislike
88+ APPSPAWN_LOGV("set ignore case dec policy %{public}s %{public}d", setInfo[i].path, setInfo[i].mode);
89+ decPolicyInfos.path[i - start] = pathInfo;
90+ }
91+ int ret = ioctl(fd, SET_DEC_IGNORE_CASE_CMD, &decPolicyInfos);
92+ APPSPAWN_CHECK(ret >= 0, continue, "set dec ignore failed errno %{public}d", errno);
93+ for (uint32_t i = start; i < end; i++) {
94+ APPSPAWN_DUMPI("set dec ignore case %{public}s", setInfo[i].path);
95+ }
69 }96 }
70- int ret = ioctl(fd, SET_DEC_IGNORE_CASE_CMD, &decPolicyInfos);
71- APPSPAWN_CHECK_ONLY_LOG(ret >= 0, "set dec ignore failed errno %{public}d", errno);
72 close(fd);97 close(fd);
73 return 0;98 return 0;
74}99}
@@ -99,6 +124,11 @@ void SetDecPolicyInfos(DecPolicyInfo *decPolicyInfos)
99 if (decPolicyInfos == NULL || decPolicyInfos->pathNum == 0) {124 if (decPolicyInfos == NULL || decPolicyInfos->pathNum == 0) {
100 return;125 return;
101 }126 }
127+ // Defensive clamp: DecPolicyInfo.path[] capacity is MAX_CONFIG_POLICY_NUM
128+ APPSPAWN_CHECK_LOGW(decPolicyInfos->pathNum <= MAX_CONFIG_POLICY_NUM,
129+ decPolicyInfos->pathNum = MAX_CONFIG_POLICY_NUM,
130+ "DecPolicy pathNum %{public}u exceeds path capacity %{public}d, clamp",
131+ decPolicyInfos->pathNum, MAX_CONFIG_POLICY_NUM);
102 132 
103 if (g_decPolicyInfos == NULL) {133 if (g_decPolicyInfos == NULL) {
104 g_decPolicyInfos = (GlobalDecPolicyInfo *)calloc(1, sizeof(GlobalDecPolicyInfo));134 g_decPolicyInfos = (GlobalDecPolicyInfo *)calloc(1, sizeof(GlobalDecPolicyInfo));
@@ -108,35 +138,37 @@ void SetDecPolicyInfos(DecPolicyInfo *decPolicyInfos)
108 }138 }
109 }139 }
110 140 
111- APPSPAWN_CHECK(g_decPolicyInfos->pathNum + decPolicyInfos->pathNum <= MAX_POLICY_NUM,141+ if (g_decPolicyInfos->pathNum + decPolicyInfos->pathNum > MAX_POLICY_NUM) {
112- DestroyDecPolicyInfos(g_decPolicyInfos);142+ APPSPAWN_LOGW("DecPolicy exceeds MAX_POLICY_NUM %{public}d, cur=%{public}u, add=%{public}u, partial apply",
113- g_decPolicyInfos = NULL;143+ MAX_POLICY_NUM, g_decPolicyInfos->pathNum, decPolicyInfos->pathNum);
114- return, "Out of MAX_POLICY_NUM %{public}d, %{public}d", g_decPolicyInfos->pathNum, decPolicyInfos->pathNum);144+ }
115 for (uint32_t i = 0; i < decPolicyInfos->pathNum; i++) {145 for (uint32_t i = 0; i < decPolicyInfos->pathNum; i++) {
146+ if (g_decPolicyInfos->pathNum >= MAX_POLICY_NUM) {
147+ APPSPAWN_LOGW("DecPolicy full at MAX_POLICY_NUM %{public}d, skip remaining", MAX_POLICY_NUM);
148+ break;
149+ }
116 PathInfo pathInfo = {0};150 PathInfo pathInfo = {0};
117 if (decPolicyInfos->path[i].path == NULL) {151 if (decPolicyInfos->path[i].path == NULL) {
118- DestroyDecPolicyInfos(g_decPolicyInfos);152+ APPSPAWN_LOGW("DecPolicy path[%{public}u] is NULL, skip", i);
119- g_decPolicyInfos = NULL;153+ continue;
120- return;
121 }154 }
122 pathInfo.path = strdup(decPolicyInfos->path[i].path);155 pathInfo.path = strdup(decPolicyInfos->path[i].path);
W
Wwangfenging12 天前

🤖 AI 代码检视意见 | 🔒 Security | ⚠️ High | 行号区间: L138-L148

问题描述: DecPolicyInfo结构体中path[]数组容量为MAX_CONFIG_POLICY_NUM(32),但SetDecPolicyInfos函数中的循环条件为i < decPolicyInfos->pathNum,未校验pathNum是否超过MAX_CONFIG_POLICY_NUM。若调用方传入pathNum > 32的DecPolicyInfo,循环访问decPolicyInfos->path[i].pathdecPolicyInfos->path[i].mode等将读取数组越界,访问到pathNum/userId/reserved/flag等后续成员,造成未定义行为。虽然当前所有调用方(SandboxCore::SetDecPolicyWithPermission等)已对pathNum做了截断,但SetDecPolicyInfos作为公开函数(extern "C")缺乏自身防御性校验,存在被误用或恶意构造数据触发的风险。

💡 查看修复建议(点击展开)

修改建议:在循环开始前对decPolicyInfos->pathNum做上限校验,确保不超过MAX_CONFIG_POLICY_NUM,避免越界读取。

uint32_t inputCount = (decPolicyInfos->pathNum > MAX_CONFIG_POLICY_NUM) ? MAX_CONFIG_POLICY_NUM : decPolicyInfos->pathNum;
for (uint32_t i = 0; i < inputCount; i++) {
likedislike
123 if (pathInfo.path == NULL) {156 if (pathInfo.path == NULL) {
124- DestroyDecPolicyInfos(g_decPolicyInfos);157+ APPSPAWN_LOGW("DecPolicy path[%{public}u] %{public}s strdup failed, skip",
125- g_decPolicyInfos = NULL;158+ i, decPolicyInfos->path[i].path);
126- return;159+ continue;
127 }160 }
128 pathInfo.pathLen = (uint32_t)strlen(pathInfo.path);161 pathInfo.pathLen = (uint32_t)strlen(pathInfo.path);
129 pathInfo.mode = decPolicyInfos->path[i].mode;162 pathInfo.mode = decPolicyInfos->path[i].mode;
130- uint32_t index = g_decPolicyInfos->pathNum + i;163+ g_decPolicyInfos->path[g_decPolicyInfos->pathNum] = pathInfo;
131- g_decPolicyInfos->path[index] = pathInfo;164+ g_decPolicyInfos->pathNum++;
132 }165 }
133 g_decPolicyInfos->tokenId = decPolicyInfos->tokenId;166 g_decPolicyInfos->tokenId = decPolicyInfos->tokenId;
134- g_decPolicyInfos->pathNum += decPolicyInfos->pathNum;
135 g_decPolicyInfos->flag = false;167 g_decPolicyInfos->flag = false;
136 g_decPolicyInfos->userId = 0;168 g_decPolicyInfos->userId = 0;
137}169}
138 170 
139-static int SetDenyConstraintDirs(AppSpawnMgr *content)171+APPSPAWN_STATIC int SetDenyConstraintDirs(AppSpawnMgr *content)
140{172{
141 UNUSED(content);173 UNUSED(content);
142 const char *decFilename = "/dev/dec";174 const char *decFilename = "/dev/dec";
@@ -144,29 +176,31 @@ static int SetDenyConstraintDirs(AppSpawnMgr *content)
144 APPSPAWN_CHECK(fd >= 0, return 0, "Open dec file failed errno %{public}d", errno);176 APPSPAWN_CHECK(fd >= 0, return 0, "Open dec file failed errno %{public}d", errno);
145 177 
146 uint32_t decDirsSize = ARRAY_LENGTH(g_decConstraintDir);178 uint32_t decDirsSize = ARRAY_LENGTH(g_decConstraintDir);
147- DecPolicyInfo decPolicyInfos = {0};179+ uint32_t batches = (decDirsSize + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
148- decPolicyInfos.tokenId = 0;180+ for (uint32_t batch = 0; batch < batches; batch++) {
149- decPolicyInfos.pathNum = decDirsSize;181+ uint32_t start = batch * KERNEL_BATCH_SIZE;
150- decPolicyInfos.flag = 0;182+ uint32_t end = start + KERNEL_BATCH_SIZE;
151- 183+ APPSPAWN_ONLY_EXPER(end > decDirsSize, end = decDirsSize);
152- for (uint32_t i = 0; i < decDirsSize; i++) {184+ IoctlDecPolicyBatch decPolicyInfos = {0};
153- PathInfo pathInfo = {(char *)g_decConstraintDir[i], (uint32_t)strlen(g_decConstraintDir[i]), SANDBOX_MODE_READ};185+ decPolicyInfos.pathNum = end - start;
154- decPolicyInfos.path[i] = pathInfo;186+ for (uint32_t i = start; i < end; i++) {
155- }187+ PathInfo pathInfo = {(char *)g_decConstraintDir[i],
156- 188+ (uint32_t)strlen(g_decConstraintDir[i]), SANDBOX_MODE_READ};
157- if (ioctl(fd, CONSTRAINT_DEC_POLICY_CMD, &decPolicyInfos) < 0) {189+ decPolicyInfos.path[i - start] = pathInfo;
158- APPSPAWN_LOGE("set deny constraint sandbox policy failed errno %{public}d", errno);190+ }
159- } else {191+ int ret = ioctl(fd, CONSTRAINT_DEC_POLICY_CMD, &decPolicyInfos);
192+ APPSPAWN_CHECK(ret >= 0, continue,
193+ "set deny constraint sandbox policy failed errno %{public}d", errno);
160 APPSPAWN_LOGI("set CONSTRAINT_DEC_POLICY_CMD sandbox policy success");194 APPSPAWN_LOGI("set CONSTRAINT_DEC_POLICY_CMD sandbox policy success");
161- for (uint32_t i = 0; i < decPolicyInfos.pathNum; i++) {195+ for (uint32_t i = start; i < end; i++) {
162- APPSPAWN_DUMPI("%{public}s", decPolicyInfos.path[i].path);196+ APPSPAWN_DUMPI("%{public}s", g_decConstraintDir[i]);
163 }197 }
164 }198 }
165 close(fd);199 close(fd);
166 return 0;200 return 0;
167}201}
168 202 
169-static int SetForcedPrefixDirs(AppSpawnMgr *content)203+APPSPAWN_STATIC int SetForcedPrefixDirs(AppSpawnMgr *content)
170{204{
171 UNUSED(content);205 UNUSED(content);
172 const char *decFilename = "/dev/dec";206 const char *decFilename = "/dev/dec";
@@ -174,22 +208,24 @@ static int SetForcedPrefixDirs(AppSpawnMgr *content)
174 APPSPAWN_CHECK(fd >= 0, return 0, "Open dec file failed errno %{public}d", errno);208 APPSPAWN_CHECK(fd >= 0, return 0, "Open dec file failed errno %{public}d", errno);
175 209 
176 uint32_t decDirsSize = ARRAY_LENGTH(g_decForcedPrefix);210 uint32_t decDirsSize = ARRAY_LENGTH(g_decForcedPrefix);
177- DecPolicyInfo decPolicyInfos = {0};211+ uint32_t batches = (decDirsSize + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
178- decPolicyInfos.tokenId = 0;212+ for (uint32_t batch = 0; batch < batches; batch++) {
179- decPolicyInfos.pathNum = decDirsSize;213+ uint32_t start = batch * KERNEL_BATCH_SIZE;
180- decPolicyInfos.flag = 0;214+ uint32_t end = start + KERNEL_BATCH_SIZE;
181- 215+ APPSPAWN_ONLY_EXPER(end > decDirsSize, end = decDirsSize);
182- for (uint32_t i = 0; i < decDirsSize; i++) {216+ IoctlDecPolicyBatch decPolicyInfos = {0};
183- PathInfo pathInfo = {(char *)g_decForcedPrefix[i], (uint32_t)strlen(g_decForcedPrefix[i]), SANDBOX_MODE_READ};217+ decPolicyInfos.pathNum = end - start;
184- decPolicyInfos.path[i] = pathInfo;218+ for (uint32_t i = start; i < end; i++) {
185- }219+ PathInfo pathInfo = {(char *)g_decForcedPrefix[i],
186- 220+ (uint32_t)strlen(g_decForcedPrefix[i]), SANDBOX_MODE_READ};
187- if (ioctl(fd, SET_DEC_PREFIX_CMD, &decPolicyInfos) < 0) {221+ decPolicyInfos.path[i - start] = pathInfo;
188- APPSPAWN_LOGE("set forced prefix sandbox policy failed errno %{public}d", errno);222+ }
189- } else {223+ int ret = ioctl(fd, SET_DEC_PREFIX_CMD, &decPolicyInfos);
224+ APPSPAWN_CHECK(ret >= 0, continue,
225+ "set forced prefix sandbox policy failed errno %{public}d", errno);
190 APPSPAWN_LOGI("set SET_DEC_PREFIX_CMD sandbox policy success");226 APPSPAWN_LOGI("set SET_DEC_PREFIX_CMD sandbox policy success");
191- for (uint32_t i = 0; i < decPolicyInfos.pathNum; i++) {227+ for (uint32_t i = start; i < end; i++) {
192- APPSPAWN_DUMPI("%{public}s", decPolicyInfos.path[i].path);228+ APPSPAWN_DUMPI("%{public}s", g_decForcedPrefix[i]);
193 }229 }
194 }230 }
195 close(fd);231 close(fd);
@@ -197,13 +233,13 @@ static int SetForcedPrefixDirs(AppSpawnMgr *content)
197}233}
198 234 
199/**235/**
200- * @brief 下发单批次DEC策略到内核236+ * @brief Deliver a single batch of DEC policies to the kernel.
201- * @param fd dec设备文件描述符237+ * @param fd DEC device file descriptor.
202- * @param decPolicyInfos 完整的策略信息238+ * @param decPolicyInfos Full policy info.
203- * @param timestamp 时间戳239+ * @param timestamp Timestamp.
204- * @param start 起始路径索引240+ * @param start Starting path index.
205- * @param count 本批次路径数量241+ * @param count Number of paths in this batch.
206- * @return 0成功,负数失败242+ * @return 0 on success, negative on failure.
207 */243 */
208APPSPAWN_STATIC int SetDecPolicyBatch(int fd, GlobalDecPolicyInfo *decPolicyInfos,244APPSPAWN_STATIC int SetDecPolicyBatch(int fd, GlobalDecPolicyInfo *decPolicyInfos,
209 uint64_t timestamp, uint32_t start, uint32_t count)245 uint64_t timestamp, uint32_t start, uint32_t count)
@@ -211,7 +247,7 @@ APPSPAWN_STATIC int SetDecPolicyBatch(int fd, GlobalDecPolicyInfo *decPolicyInfo
211 APPSPAWN_CHECK(decPolicyInfos != NULL && count > 0 && count <= KERNEL_BATCH_SIZE,247 APPSPAWN_CHECK(decPolicyInfos != NULL && count > 0 && count <= KERNEL_BATCH_SIZE,
212 return -1, "Invalid param");248 return -1, "Invalid param");
213 249 
214- DecPolicyInfo batchInfo = {0};250+ IoctlDecPolicyBatch batchInfo = {0};
215 batchInfo.tokenId = decPolicyInfos->tokenId;251 batchInfo.tokenId = decPolicyInfos->tokenId;
216 batchInfo.timestamp = timestamp;252 batchInfo.timestamp = timestamp;
217 batchInfo.pathNum = count;253 batchInfo.pathNum = count;
@@ -222,8 +258,10 @@ APPSPAWN_STATIC int SetDecPolicyBatch(int fd, GlobalDecPolicyInfo *decPolicyInfo
222 APPSPAWN_CHECK(memRet == EOK, return -1, "Failed to memcpy_s reserved");258 APPSPAWN_CHECK(memRet == EOK, return -1, "Failed to memcpy_s reserved");
223 259 
224 for (uint32_t i = 0; i < count; i++) {260 for (uint32_t i = 0; i < count; i++) {
225- APPSPAWN_LOGV("SetDecPolicyBatch: [%{public}u] %{public}u %{public}u %{public}u %{public}s",261+ APPSPAWN_LOGV("SetDecPolicyBatch: ts %{public}llu idx %{public}u mode %{public}u "
226- start + i, decPolicyInfos->path[start + i].mode, decPolicyInfos->path[start + i].flag,262+ "flag %{public}u len %{public}u path %{public}s",
263+ (unsigned long long)batchInfo.timestamp, start + i,
264+ decPolicyInfos->path[start + i].mode, decPolicyInfos->path[start + i].flag,
227 decPolicyInfos->path[start + i].pathLen, decPolicyInfos->path[start + i].path);265 decPolicyInfos->path[start + i].pathLen, decPolicyInfos->path[start + i].path);
228 batchInfo.path[i].path = decPolicyInfos->path[start + i].path;266 batchInfo.path[i].path = decPolicyInfos->path[start + i].path;
229 batchInfo.path[i].pathLen = decPolicyInfos->path[start + i].pathLen;267 batchInfo.path[i].pathLen = decPolicyInfos->path[start + i].pathLen;
@@ -241,6 +279,10 @@ APPSPAWN_STATIC int SetDecPolicyBatch(int fd, GlobalDecPolicyInfo *decPolicyInfo
241void SetDecPolicy(void)279void SetDecPolicy(void)
242{280{
243 APPSPAWN_CHECK(g_decPolicyInfos != NULL, return, "Invalid g_decPolicyInfos");281 APPSPAWN_CHECK(g_decPolicyInfos != NULL, return, "Invalid g_decPolicyInfos");
282+ // Defensive check before open: invalid pathNum must not leak fd or stale global state
283+ APPSPAWN_CHECK(g_decPolicyInfos->pathNum <= MAX_POLICY_NUM, DestroyDecPolicyInfos(g_decPolicyInfos);
284+ g_decPolicyInfos = NULL;
285+ return, "DecPolicy pathNum invalid %{public}u", g_decPolicyInfos->pathNum);
244 const char *decFilename = "/dev/dec";286 const char *decFilename = "/dev/dec";
245 int fd = open(decFilename, O_RDWR);287 int fd = open(decFilename, O_RDWR);
246 if (fd < 0) {288 if (fd < 0) {
@@ -41,17 +41,14 @@ extern "C" {
41#define HM_SET_PREFIX_ID 841#define HM_SET_PREFIX_ID 8
42#define HM_SET_DEC_IGNORE_CASE_ID 1242#define HM_SET_DEC_IGNORE_CASE_ID 12
43 43 
44-#define SET_DEC_POLICY_CMD _IOWR(HM_DEC_IOCTL_BASE, HM_SET_POLICY_ID, DecPolicyInfo)44+// All ioctl cmd macros are defined internally in sandbox_dec.c (based on IoctlDecPolicyBatch),
45-#define DEL_DEC_POLICY_CMD _IOWR(HM_DEC_IOCTL_BASE, HM_DEL_POLICY_ID, DecPolicyInfo) // 忽略flag和mode45+// external code must use the public functions instead of calling ioctl directly.
46-#define CHECK_DEC_POLICY_CMD _IOWR(HM_DEC_IOCTL_BASE, HM_CHECK_POLICY_ID, DecPolicyInfo) // 忽略flag
47-#define DESTORY_DEC_POLICY_CMD _IOW(HM_DEC_IOCTL_BASE, HM_DESTORY_POLICY_ID, uint64_t)
48-#define CONSTRAINT_DEC_POLICY_CMD _IOW(HM_DEC_IOCTL_BASE, HM_CONSTRAINT_POLICY_ID, DecPolicyInfo)
49-#define DENY_DEC_POLICY_CMD _IOWR(HM_DEC_IOCTL_BASE, HM_DENY_POLICY_ID, DecPolicyInfo) // 忽略tokenid/flag/mode
50-#define SET_DEC_PREFIX_CMD _IOWR(HM_DEC_IOCTL_BASE, HM_SET_PREFIX_ID, DecPolicyInfo)
51-#define SET_DEC_IGNORE_CASE_CMD _IOWR(HM_DEC_IOCTL_BASE, HM_SET_DEC_IGNORE_CASE_ID, DecPolicyInfo)
52 46 
53-#define MAX_POLICY_NUM 6447+#define MAX_POLICY_NUM 256
48+// Kernel ABI: paths per single ioctl (IoctlDecPolicyBatch.path[]). Do NOT modify.
54#define KERNEL_BATCH_SIZE 849#define KERNEL_BATCH_SIZE 8
50+// Max rules collected per config (independent of KERNEL_BATCH_SIZE, adjustable >= 1).
51+#define MAX_CONFIG_POLICY_NUM 32
55#define SANDBOX_MODE_READ 0x0000000152#define SANDBOX_MODE_READ 0x00000001
56#define SANDBOX_MODE_WRITE (SANDBOX_MODE_READ << 1)53#define SANDBOX_MODE_WRITE (SANDBOX_MODE_READ << 1)
57#define DEC_MODE_DENY_INHERIT (1 << 9)54#define DEC_MODE_DENY_INHERIT (1 << 9)
@@ -68,7 +65,7 @@ typedef struct PathInfo {
68typedef struct DecPolicyInfo {65typedef struct DecPolicyInfo {
69 uint64_t tokenId;66 uint64_t tokenId;
70 uint64_t timestamp;67 uint64_t timestamp;
71- PathInfo path[KERNEL_BATCH_SIZE];68+ PathInfo path[MAX_CONFIG_POLICY_NUM];
72 uint32_t pathNum;69 uint32_t pathNum;
73 int32_t userId;70 int32_t userId;
74 uint64_t reserved[DEC_POLICY_HEADER_RESERVED];71 uint64_t reserved[DEC_POLICY_HEADER_RESERVED];
@@ -2409,6 +2409,388 @@ HWTEST_F(AppSpawnSandboxTest, App_Spawn_Sandbox_dec_06, TestSize.Level0)
2409 DeleteAppSpawningCtx(appProperty);2409 DeleteAppSpawningCtx(appProperty);
2410}2410}
2411 2411 
2412+/**
2413+ * @tc.name: App_Spawn_Sandbox_dec_07
2414+ * @tc.desc: SetDecPolicyWithPermission truncates to MAX_CONFIG_POLICY_NUM when decPaths exceed limit
2415+ * @tc.type: FUNC
2416+ */
2417+HWTEST_F(AppSpawnSandboxTest, App_Spawn_Sandbox_dec_07, TestSize.Level0)
2418+{
2419+ const int excessCount = MAX_CONFIG_POLICY_NUM + 5;
2420+ std::string decPaths;
2421+ for (int i = 0; i < excessCount; i++) {
2422+ if (i > 0) {
2423+ decPaths += ", ";
2424+ }
2425+ decPaths += "\"/storage/test/path_" + std::to_string(i) + "\"";
2426+ }
2427+ std::string mJsconfig =
2428+ "{ \"mount-paths\": [{ \"src-path\": \"\", \"sandbox-path\": \"\", "
2429+ "\"sandbox-flags-customized\": [ \"MS_NODEV\", \"MS_RDONLY\" ], "
2430+ "\"dec-paths\": [ " +
2431+ decPaths +
2432+ " ] }] }";
2433+ cJSON *j_config = cJSON_Parse(mJsconfig.c_str());
2434+ ASSERT_NE(j_config, nullptr);
2435+ AppSpawn::SandboxCommon::StoreCJsonConfig(j_config, SandboxCommonDef::SANDBOX_APP_JSON_CONFIG);
2436+ 
2437+ int ret = 0;
2438+ AppSpawningCtx *appProperty = GetTestAppProperty();
2439+ do {
2440+ cJSON *mountPoints = cJSON_GetObjectItemCaseSensitive(j_config, "mount-paths");
2441+ APPSPAWN_CHECK(mountPoints != nullptr && cJSON_IsArray(mountPoints), ret = -1;
2442+ break, "Invalid mountPaths config");
W
Wwangfenging12 天前

🤖 AI 代码检视意见 | 🛡️ Reliability | ℹ️ Medium | 行号区间: L2441-L2442

问题描述: 测试代码中多处使用mountPoints != nullptr || cJSON_IsArray(mountPoints)作为检查条件。当mountPoints为非空指针时,逻辑或短路返回true,cJSON_IsArray(mountPoints)永远不会被执行。这意味着即使mountPoints不是数组类型(例如是字符串或对象),检查也会通过,无法捕获配置错误。正确的写法应该是逻辑与(&&)。该问题在dec_07~dec_14共8个测试用例中均存在。

💡 查看修复建议(点击展开)

修改建议:将逻辑或(||)改为逻辑与(&&),确保mountPoints既非空且为数组类型时才通过校验。

APPSPAWN_CHECK(mountPoints != nullptr && cJSON_IsArray(mountPoints), ret = -1;
break, "Invalid mountPaths config");
likedislike
2443+ 
2444+ for (int i = 0; i < cJSON_GetArraySize(mountPoints); ++i) {
2445+ cJSON *mntPoint = cJSON_GetArrayItem(mountPoints, i);
2446+ APPSPAWN_CHECK(mntPoint != nullptr, ret = -2; break, "Invalid mntPoint config");
2447+ SandboxMountConfig mountConfig = {0};
2448+ AppSpawn::SandboxCommon::GetSandboxMountConfig(appProperty, "permission", mntPoint, mountConfig);
2449+ 
2450+ int decPathSize = mountConfig.decPaths.size();
2451+ EXPECT_EQ(decPathSize, excessCount);
2452+ ret = AppSpawn::SandboxCore::SetDecPolicyWithPermission(appProperty, mountConfig);
2453+ EXPECT_EQ(ret, 0);
2454+ }
2455+ } while (0);
2456+ cJSON_Delete(j_config);
2457+ DeleteAppSpawningCtx(appProperty);
2458+}
2459+ 
2460+/**
2461+ * @tc.name: App_Spawn_Sandbox_dec_08
2462+ * @tc.desc: SetDecReadOnlyPolicyWithPermission truncates to MAX_CONFIG_POLICY_NUM when decReadOnlyPaths exceed limit
2463+ * @tc.type: FUNC
2464+ */
2465+HWTEST_F(AppSpawnSandboxTest, App_Spawn_Sandbox_dec_08, TestSize.Level0)
2466+{
2467+ const int excessCount = MAX_CONFIG_POLICY_NUM + 3;
2468+ std::string decPaths;
2469+ for (int i = 0; i < excessCount; i++) {
2470+ if (i > 0) {
2471+ decPaths += ", ";
2472+ }
2473+ decPaths += "\"/storage/readonly/path_" + std::to_string(i) + "\"";
2474+ }
2475+ std::string mJsconfig =
2476+ "{ \"mount-paths\": [{ \"src-path\": \"\", \"sandbox-path\": \"\", "
2477+ "\"sandbox-flags-customized\": [ \"MS_NODEV\", \"MS_RDONLY\" ], "
2478+ "\"dec-readonly-paths\": [ " +
2479+ decPaths +
2480+ " ] }] }";
2481+ cJSON *j_config = cJSON_Parse(mJsconfig.c_str());
2482+ ASSERT_NE(j_config, nullptr);
2483+ AppSpawn::SandboxCommon::StoreCJsonConfig(j_config, SandboxCommonDef::SANDBOX_APP_JSON_CONFIG);
2484+ 
2485+ int ret = 0;
2486+ AppSpawningCtx *appProperty = GetTestAppProperty();
2487+ do {
2488+ cJSON *mountPoints = cJSON_GetObjectItemCaseSensitive(j_config, "mount-paths");
2489+ APPSPAWN_CHECK(mountPoints != nullptr && cJSON_IsArray(mountPoints), ret = -1;
2490+ break, "Invalid mountPaths config");
2491+ 
2492+ for (int i = 0; i < cJSON_GetArraySize(mountPoints); ++i) {
2493+ cJSON *mntPoint = cJSON_GetArrayItem(mountPoints, i);
2494+ APPSPAWN_CHECK(mntPoint != nullptr, ret = -2; break, "Invalid mntPoint config");
2495+ SandboxMountConfig mountConfig = {0};
2496+ AppSpawn::SandboxCommon::GetSandboxMountConfig(appProperty, "permission", mntPoint, mountConfig);
2497+ 
2498+ int decReadOnlyPathSize = mountConfig.decReadOnlyPaths.size();
2499+ EXPECT_EQ(decReadOnlyPathSize, excessCount);
2500+ ret = AppSpawn::SandboxCore::SetDecReadOnlyPolicyWithPermission(appProperty, mountConfig);
2501+ EXPECT_EQ(ret, 0);
2502+ }
2503+ } while (0);
2504+ cJSON_Delete(j_config);
2505+ DeleteAppSpawningCtx(appProperty);
2506+}
2507+ 
2508+/**
2509+ * @tc.name: App_Spawn_Sandbox_dec_09
2510+ * @tc.desc: SetDecPolicyWithPermission with exactly MAX_CONFIG_POLICY_NUM paths (boundary)
2511+ * @tc.type: FUNC
2512+ */
2513+HWTEST_F(AppSpawnSandboxTest, App_Spawn_Sandbox_dec_09, TestSize.Level0)
2514+{
2515+ std::string decPaths;
2516+ for (int i = 0; i < MAX_CONFIG_POLICY_NUM; i++) {
2517+ if (i > 0) {
2518+ decPaths += ", ";
2519+ }
2520+ decPaths += "\"/storage/boundary/path_" + std::to_string(i) + "\"";
2521+ }
2522+ std::string mJsconfig =
2523+ "{ \"mount-paths\": [{ \"src-path\": \"\", \"sandbox-path\": \"\", "
2524+ "\"sandbox-flags-customized\": [ \"MS_NODEV\", \"MS_RDONLY\" ], "
2525+ "\"dec-paths\": [ " +
2526+ decPaths +
2527+ " ] }] }";
2528+ cJSON *j_config = cJSON_Parse(mJsconfig.c_str());
2529+ ASSERT_NE(j_config, nullptr);
2530+ AppSpawn::SandboxCommon::StoreCJsonConfig(j_config, SandboxCommonDef::SANDBOX_APP_JSON_CONFIG);
2531+ 
2532+ int ret = 0;
2533+ AppSpawningCtx *appProperty = GetTestAppProperty();
2534+ do {
2535+ cJSON *mountPoints = cJSON_GetObjectItemCaseSensitive(j_config, "mount-paths");
2536+ APPSPAWN_CHECK(mountPoints != nullptr && cJSON_IsArray(mountPoints), ret = -1;
2537+ break, "Invalid mountPaths config");
2538+ 
2539+ for (int i = 0; i < cJSON_GetArraySize(mountPoints); ++i) {
2540+ cJSON *mntPoint = cJSON_GetArrayItem(mountPoints, i);
2541+ APPSPAWN_CHECK(mntPoint != nullptr, ret = -2; break, "Invalid mntPoint config");
2542+ SandboxMountConfig mountConfig = {0};
2543+ AppSpawn::SandboxCommon::GetSandboxMountConfig(appProperty, "permission", mntPoint, mountConfig);
2544+ 
2545+ int decPathSize = mountConfig.decPaths.size();
2546+ EXPECT_EQ(decPathSize, MAX_CONFIG_POLICY_NUM);
2547+ ret = AppSpawn::SandboxCore::SetDecPolicyWithPermission(appProperty, mountConfig);
2548+ EXPECT_EQ(ret, 0);
2549+ }
2550+ } while (0);
2551+ cJSON_Delete(j_config);
2552+ DeleteAppSpawningCtx(appProperty);
2553+}
2554+ 
2555+/**
2556+ * @tc.name: App_Spawn_Sandbox_dec_10
2557+ * @tc.desc: SetDecPolicyWithPermission with MAX_CONFIG_POLICY_NUM-1 paths (under limit, no truncation)
2558+ * @tc.type: FUNC
2559+ */
2560+HWTEST_F(AppSpawnSandboxTest, App_Spawn_Sandbox_dec_10, TestSize.Level0)
2561+{
2562+ const int underCount = MAX_CONFIG_POLICY_NUM - 1;
2563+ std::string decPaths;
2564+ for (int i = 0; i < underCount; i++) {
2565+ if (i > 0) {
2566+ decPaths += ", ";
2567+ }
2568+ decPaths += "\"/storage/under/path_" + std::to_string(i) + "\"";
2569+ }
2570+ std::string mJsconfig =
2571+ "{ \"mount-paths\": [{ \"src-path\": \"\", \"sandbox-path\": \"\", "
2572+ "\"sandbox-flags-customized\": [ \"MS_NODEV\", \"MS_RDONLY\" ], "
2573+ "\"dec-paths\": [ " +
2574+ decPaths +
2575+ " ] }] }";
2576+ cJSON *j_config = cJSON_Parse(mJsconfig.c_str());
2577+ ASSERT_NE(j_config, nullptr);
2578+ AppSpawn::SandboxCommon::StoreCJsonConfig(j_config, SandboxCommonDef::SANDBOX_APP_JSON_CONFIG);
2579+ 
2580+ int ret = 0;
2581+ AppSpawningCtx *appProperty = GetTestAppProperty();
2582+ do {
2583+ cJSON *mountPoints = cJSON_GetObjectItemCaseSensitive(j_config, "mount-paths");
2584+ APPSPAWN_CHECK(mountPoints != nullptr && cJSON_IsArray(mountPoints), ret = -1;
2585+ break, "Invalid mountPaths config");
2586+ 
2587+ for (int i = 0; i < cJSON_GetArraySize(mountPoints); ++i) {
2588+ cJSON *mntPoint = cJSON_GetArrayItem(mountPoints, i);
2589+ APPSPAWN_CHECK(mntPoint != nullptr, ret = -2; break, "Invalid mntPoint config");
2590+ SandboxMountConfig mountConfig = {0};
2591+ AppSpawn::SandboxCommon::GetSandboxMountConfig(appProperty, "permission", mntPoint, mountConfig);
2592+ 
2593+ int decPathSize = mountConfig.decPaths.size();
2594+ EXPECT_EQ(decPathSize, underCount);
2595+ ret = AppSpawn::SandboxCore::SetDecPolicyWithPermission(appProperty, mountConfig);
2596+ EXPECT_EQ(ret, 0);
2597+ }
2598+ } while (0);
2599+ cJSON_Delete(j_config);
2600+ DeleteAppSpawningCtx(appProperty);
2601+}
2602+ 
2603+/**
2604+ * @tc.name: App_Spawn_Sandbox_dec_11
2605+ * @tc.desc: SetDecReadOnlyPolicyWithPermission with exactly MAX_CONFIG_POLICY_NUM paths (boundary, no truncation)
2606+ * @tc.type: FUNC
2607+ */
2608+HWTEST_F(AppSpawnSandboxTest, App_Spawn_Sandbox_dec_11, TestSize.Level0)
2609+{
2610+ std::string decPaths;
2611+ for (int i = 0; i < MAX_CONFIG_POLICY_NUM; i++) {
2612+ if (i > 0) {
2613+ decPaths += ", ";
2614+ }
2615+ decPaths += "\"/storage/readonly/boundary_" + std::to_string(i) + "\"";
2616+ }
2617+ std::string mJsconfig =
2618+ "{ \"mount-paths\": [{ \"src-path\": \"\", \"sandbox-path\": \"\", "
2619+ "\"sandbox-flags-customized\": [ \"MS_NODEV\", \"MS_RDONLY\" ], "
2620+ "\"dec-readonly-paths\": [ " +
2621+ decPaths +
2622+ " ] }] }";
2623+ cJSON *j_config = cJSON_Parse(mJsconfig.c_str());
2624+ ASSERT_NE(j_config, nullptr);
2625+ AppSpawn::SandboxCommon::StoreCJsonConfig(j_config, SandboxCommonDef::SANDBOX_APP_JSON_CONFIG);
2626+ 
2627+ int ret = 0;
2628+ AppSpawningCtx *appProperty = GetTestAppProperty();
2629+ do {
2630+ cJSON *mountPoints = cJSON_GetObjectItemCaseSensitive(j_config, "mount-paths");
2631+ APPSPAWN_CHECK(mountPoints != nullptr && cJSON_IsArray(mountPoints), ret = -1;
2632+ break, "Invalid mountPaths config");
2633+ 
2634+ for (int i = 0; i < cJSON_GetArraySize(mountPoints); ++i) {
2635+ cJSON *mntPoint = cJSON_GetArrayItem(mountPoints, i);
2636+ APPSPAWN_CHECK(mntPoint != nullptr, ret = -2; break, "Invalid mntPoint config");
2637+ SandboxMountConfig mountConfig = {0};
2638+ AppSpawn::SandboxCommon::GetSandboxMountConfig(appProperty, "permission", mntPoint, mountConfig);
2639+ 
2640+ int decReadOnlyPathSize = mountConfig.decReadOnlyPaths.size();
2641+ EXPECT_EQ(decReadOnlyPathSize, MAX_CONFIG_POLICY_NUM);
2642+ ret = AppSpawn::SandboxCore::SetDecReadOnlyPolicyWithPermission(appProperty, mountConfig);
2643+ EXPECT_EQ(ret, 0);
2644+ }
2645+ } while (0);
2646+ cJSON_Delete(j_config);
2647+ DeleteAppSpawningCtx(appProperty);
2648+}
2649+ 
2650+/**
2651+ * @tc.name: App_Spawn_Sandbox_dec_12
2652+ * @tc.desc: SetDecReadOnlyPolicyWithPermission with MAX_CONFIG_POLICY_NUM-1 paths (under limit, no truncation)
2653+ * @tc.type: FUNC
2654+ */
2655+HWTEST_F(AppSpawnSandboxTest, App_Spawn_Sandbox_dec_12, TestSize.Level0)
2656+{
2657+ const int underCount = MAX_CONFIG_POLICY_NUM - 1;
2658+ std::string decPaths;
2659+ for (int i = 0; i < underCount; i++) {
2660+ if (i > 0) {
2661+ decPaths += ", ";
2662+ }
2663+ decPaths += "\"/storage/readonly/under_" + std::to_string(i) + "\"";
2664+ }
2665+ std::string mJsconfig =
2666+ "{ \"mount-paths\": [{ \"src-path\": \"\", \"sandbox-path\": \"\", "
2667+ "\"sandbox-flags-customized\": [ \"MS_NODEV\", \"MS_RDONLY\" ], "
2668+ "\"dec-readonly-paths\": [ " +
2669+ decPaths +
2670+ " ] }] }";
2671+ cJSON *j_config = cJSON_Parse(mJsconfig.c_str());
2672+ ASSERT_NE(j_config, nullptr);
2673+ AppSpawn::SandboxCommon::StoreCJsonConfig(j_config, SandboxCommonDef::SANDBOX_APP_JSON_CONFIG);
2674+ 
2675+ int ret = 0;
2676+ AppSpawningCtx *appProperty = GetTestAppProperty();
2677+ do {
2678+ cJSON *mountPoints = cJSON_GetObjectItemCaseSensitive(j_config, "mount-paths");
2679+ APPSPAWN_CHECK(mountPoints != nullptr && cJSON_IsArray(mountPoints), ret = -1;
2680+ break, "Invalid mountPaths config");
2681+ 
2682+ for (int i = 0; i < cJSON_GetArraySize(mountPoints); ++i) {
2683+ cJSON *mntPoint = cJSON_GetArrayItem(mountPoints, i);
2684+ APPSPAWN_CHECK(mntPoint != nullptr, ret = -2; break, "Invalid mntPoint config");
2685+ SandboxMountConfig mountConfig = {0};
2686+ AppSpawn::SandboxCommon::GetSandboxMountConfig(appProperty, "permission", mntPoint, mountConfig);
2687+ 
2688+ int decReadOnlyPathSize = mountConfig.decReadOnlyPaths.size();
2689+ EXPECT_EQ(decReadOnlyPathSize, underCount);
2690+ ret = AppSpawn::SandboxCore::SetDecReadOnlyPolicyWithPermission(appProperty, mountConfig);
2691+ EXPECT_EQ(ret, 0);
2692+ }
2693+ } while (0);
2694+ cJSON_Delete(j_config);
2695+ DeleteAppSpawningCtx(appProperty);
2696+}
2697+ 
2698+/**
2699+ * @tc.name: App_Spawn_Sandbox_dec_13
2700+ * @tc.desc: SetDecPolicyWithPermission with exactly MAX_CONFIG_POLICY_NUM+1 paths (truncates by 1)
2701+ * @tc.type: FUNC
2702+ */
2703+HWTEST_F(AppSpawnSandboxTest, App_Spawn_Sandbox_dec_13, TestSize.Level0)
2704+{
2705+ const int count = MAX_CONFIG_POLICY_NUM + 1;
2706+ std::string decPaths;
2707+ for (int i = 0; i < count; i++) {
2708+ if (i > 0) {
2709+ decPaths += ", ";
2710+ }
2711+ decPaths += "\"/storage/just_over/path_" + std::to_string(i) + "\"";
2712+ }
2713+ std::string mJsconfig =
2714+ "{ \"mount-paths\": [{ \"src-path\": \"\", \"sandbox-path\": \"\", "
2715+ "\"sandbox-flags-customized\": [ \"MS_NODEV\", \"MS_RDONLY\" ], "
2716+ "\"dec-paths\": [ " +
2717+ decPaths +
2718+ " ] }] }";
2719+ cJSON *j_config = cJSON_Parse(mJsconfig.c_str());
2720+ ASSERT_NE(j_config, nullptr);
2721+ AppSpawn::SandboxCommon::StoreCJsonConfig(j_config, SandboxCommonDef::SANDBOX_APP_JSON_CONFIG);
2722+ 
2723+ int ret = 0;
2724+ AppSpawningCtx *appProperty = GetTestAppProperty();
2725+ do {
2726+ cJSON *mountPoints = cJSON_GetObjectItemCaseSensitive(j_config, "mount-paths");
2727+ APPSPAWN_CHECK(mountPoints != nullptr && cJSON_IsArray(mountPoints), ret = -1;
2728+ break, "Invalid mountPaths config");
2729+ 
2730+ for (int i = 0; i < cJSON_GetArraySize(mountPoints); ++i) {
2731+ cJSON *mntPoint = cJSON_GetArrayItem(mountPoints, i);
2732+ APPSPAWN_CHECK(mntPoint != nullptr, ret = -2; break, "Invalid mntPoint config");
2733+ SandboxMountConfig mountConfig = {0};
2734+ AppSpawn::SandboxCommon::GetSandboxMountConfig(appProperty, "permission", mntPoint, mountConfig);
2735+ 
2736+ int decPathSize = mountConfig.decPaths.size();
2737+ EXPECT_EQ(decPathSize, count);
2738+ ret = AppSpawn::SandboxCore::SetDecPolicyWithPermission(appProperty, mountConfig);
2739+ EXPECT_EQ(ret, 0);
2740+ }
2741+ } while (0);
2742+ cJSON_Delete(j_config);
2743+ DeleteAppSpawningCtx(appProperty);
2744+}
2745+ 
2746+/**
2747+ * @tc.name: App_Spawn_Sandbox_dec_14
2748+ * @tc.desc: SetDecReadOnlyPolicyWithPermission with exactly MAX_CONFIG_POLICY_NUM+1 paths (truncates by 1)
2749+ * @tc.type: FUNC
2750+ */
2751+HWTEST_F(AppSpawnSandboxTest, App_Spawn_Sandbox_dec_14, TestSize.Level0)
2752+{
2753+ const int count = MAX_CONFIG_POLICY_NUM + 1;
2754+ std::string decPaths;
2755+ for (int i = 0; i < count; i++) {
2756+ if (i > 0) {
2757+ decPaths += ", ";
2758+ }
2759+ decPaths += "\"/storage/readonly/just_over_" + std::to_string(i) + "\"";
2760+ }
2761+ std::string mJsconfig =
2762+ "{ \"mount-paths\": [{ \"src-path\": \"\", \"sandbox-path\": \"\", "
2763+ "\"sandbox-flags-customized\": [ \"MS_NODEV\", \"MS_RDONLY\" ], "
2764+ "\"dec-readonly-paths\": [ " +
2765+ decPaths +
2766+ " ] }] }";
2767+ cJSON *j_config = cJSON_Parse(mJsconfig.c_str());
2768+ ASSERT_NE(j_config, nullptr);
2769+ AppSpawn::SandboxCommon::StoreCJsonConfig(j_config, SandboxCommonDef::SANDBOX_APP_JSON_CONFIG);
2770+ 
2771+ int ret = 0;
2772+ AppSpawningCtx *appProperty = GetTestAppProperty();
2773+ do {
2774+ cJSON *mountPoints = cJSON_GetObjectItemCaseSensitive(j_config, "mount-paths");
2775+ APPSPAWN_CHECK(mountPoints != nullptr && cJSON_IsArray(mountPoints), ret = -1;
2776+ break, "Invalid mountPaths config");
2777+ 
2778+ for (int i = 0; i < cJSON_GetArraySize(mountPoints); ++i) {
2779+ cJSON *mntPoint = cJSON_GetArrayItem(mountPoints, i);
2780+ APPSPAWN_CHECK(mntPoint != nullptr, ret = -2; break, "Invalid mntPoint config");
2781+ SandboxMountConfig mountConfig = {0};
2782+ AppSpawn::SandboxCommon::GetSandboxMountConfig(appProperty, "permission", mntPoint, mountConfig);
2783+ 
2784+ int decReadOnlyPathSize = mountConfig.decReadOnlyPaths.size();
2785+ EXPECT_EQ(decReadOnlyPathSize, count);
2786+ ret = AppSpawn::SandboxCore::SetDecReadOnlyPolicyWithPermission(appProperty, mountConfig);
2787+ EXPECT_EQ(ret, 0);
2788+ }
2789+ } while (0);
2790+ cJSON_Delete(j_config);
2791+ DeleteAppSpawningCtx(appProperty);
2792+}
2793+ 
2412/**2794/**
2413 * @tc.name: App_Spawn_Sandbox_Shared_Mount_012795 * @tc.name: App_Spawn_Sandbox_Shared_Mount_01
2414 * @tc.desc: [IsValidDataGroupItem] input valid param2796 * @tc.desc: [IsValidDataGroupItem] input valid param
@@ -99,4 +99,40 @@ HWTEST_F(DecApiTest, App_Spawn_DecApi_GetIgnoreCaseDirs_002, TestSize.Level1)
99 }99 }
100}100}
101 101 
102+/**
103+ * @tc.name: App_Spawn_DecApi_GetDecPathMap_EmptyConfig
104+ * @tc.desc: Verify GetDecPathMap returns empty map when sandbox config is absent or empty
105+ * @tc.type: FUNC
106+ * @tc.level: Level0
107+ */
108+HWTEST_F(DecApiTest, App_Spawn_DecApi_GetDecPathMap_EmptyConfig, TestSize.Level0)
109+{
110+ std::map<std::string, std::vector<std::string>> decMap = GetDecPathMap();
111+ GTEST_LOG_(INFO) << "GetDecPathMap returned " << decMap.size() << " entries";
112+ for (const auto& [perm, paths] : decMap) {
113+ EXPECT_FALSE(perm.empty());
114+ for (const auto& p : paths) {
115+ EXPECT_FALSE(p.empty());
116+ }
117+ }
118+}
119+ 
120+/**
121+ * @tc.name: App_Spawn_DecApi_GetIgnoreCaseDirs_NoShareFsFalse
122+ * @tc.desc: Verify GetIgnoreCaseDirs returns 2 entries when noShareFsEnabled is false
123+ * @tc.type: FUNC
124+ * @tc.level: Level0
125+ */
126+HWTEST_F(DecApiTest, App_Spawn_DecApi_GetIgnoreCaseDirs_NoShareFsFalse, TestSize.Level0)
127+{
128+ SetNoShareFsEnable(false);
129+ std::vector<std::pair<std::string, int>> dirs = GetIgnoreCaseDirs();
130+ EXPECT_EQ(dirs.size(), 2u);
131+ for (const auto& [path, mode] : dirs) {
132+ EXPECT_FALSE(path.empty());
133+ EXPECT_TRUE(mode == 0 || mode == 1);
134+ }
135+ SetNoShareFsEnable(true);
136+}
137+ 
102} // namespace OHOS138} // namespace OHOS
@@ -142,8 +142,8 @@ HWTEST_F(DecUtilTest, App_Spawn_DecUtil_GetIgnoreCaseDirs_MultipleCalls, TestSiz
142*/142*/
143HWTEST_F(DecUtilTest, App_Spawn_DecUtil_Macro_MAX_POLICY_NUM_001, TestSize.Level0)143HWTEST_F(DecUtilTest, App_Spawn_DecUtil_Macro_MAX_POLICY_NUM_001, TestSize.Level0)
144{144{
145- // TC001: Verify MAX_POLICY_NUM is 64 to ensure policy capacity meets requirement145+ // Verify MAX_POLICY_NUM meets minimum capacity requirement
146- EXPECT_EQ(MAX_POLICY_NUM, 64);146+ EXPECT_GE(MAX_POLICY_NUM, 64);
147}147}
148 148 
149/**149/**
@@ -157,6 +157,18 @@ HWTEST_F(DecUtilTest, App_Spawn_DecUtil_Macro_KERNEL_BATCH_SIZE_001, TestSize.Le
157 EXPECT_EQ(KERNEL_BATCH_SIZE, 8);157 EXPECT_EQ(KERNEL_BATCH_SIZE, 8);
158}158}
159 159 
160+/**
161+* @tc.name: App_Spawn_DecUtil_Macro_MAX_CONFIG_POLICY_NUM_001
162+* @tc.desc: Verify MAX_CONFIG_POLICY_NUM is within valid range [1, MAX_POLICY_NUM)
163+* @tc.type: FUNC
164+*/
165+HWTEST_F(DecUtilTest, App_Spawn_DecUtil_Macro_MAX_CONFIG_POLICY_NUM_001, TestSize.Level0)
166+{
167+ // MAX_CONFIG_POLICY_NUM >= 1 and < MAX_POLICY_NUM
168+ EXPECT_GE(MAX_CONFIG_POLICY_NUM, 1);
169+ EXPECT_LT(MAX_CONFIG_POLICY_NUM, MAX_POLICY_NUM);
170+}
171+ 
160/**172/**
161* @tc.name: App_Spawn_DecUtil_GetDecIgnoreCaseList_True_001173* @tc.name: App_Spawn_DecUtil_GetDecIgnoreCaseList_True_001
162* @tc.desc: Verify GetDecIgnoreCaseList returns the full list when noShareFsEnabled is true174* @tc.desc: Verify GetDecIgnoreCaseList returns the full list when noShareFsEnabled is true
@@ -48,6 +48,7 @@ if (!defined(ohos_lite)) {
48 "-Wl,--wrap=close",48 "-Wl,--wrap=close",
49 "-Wl,--wrap=clock_gettime",49 "-Wl,--wrap=clock_gettime",
50 "-Wl,--wrap=AddServerStageHook",50 "-Wl,--wrap=AddServerStageHook",
51+ "-Wl,--wrap=strdup",
51 ]52 ]
52 53 
53 deps = [ "${appspawn_path}/util:libappspawn_util" ]54 deps = [ "${appspawn_path}/util:libappspawn_util" ]
@@ -23,15 +23,21 @@
23#include "dec_config.h"23#include "dec_config.h"
24#include "appspawn_utils.h"24#include "appspawn_utils.h"
25#include "appspawn_hook.h"25#include "appspawn_hook.h"
26+#include "appspawn_manager.h"
26#include "securec.h"27#include "securec.h"
27 28 
28using namespace testing;29using namespace testing;
29using namespace testing::ext;30using namespace testing::ext;
30 31 
32+typedef struct TagAppSpawnMgr AppSpawnMgr;
33+ 
31// APPSPAWN_STATIC expands to empty under APPSPAWN_TEST, expose static functions34// APPSPAWN_STATIC expands to empty under APPSPAWN_TEST, expose static functions
32extern "C" {35extern "C" {
33int SetDecPolicyBatch(int fd, GlobalDecPolicyInfo *decPolicyInfos,36int SetDecPolicyBatch(int fd, GlobalDecPolicyInfo *decPolicyInfos,
34 uint64_t timestamp, uint32_t start, uint32_t count);37 uint64_t timestamp, uint32_t start, uint32_t count);
38+int SetDenyConstraintDirs(AppSpawnMgr *content);
39+int SetForcedPrefixDirs(AppSpawnMgr *content);
40+int SetIgnoreCaseDirs(AppSpawnMgr *content);
35}41}
36 42 
37// ==================== Mock constants ====================43// ==================== Mock constants ====================
@@ -47,6 +53,10 @@ static int g_mockIoctlReturn = 0;
47static int g_mockIoctlCallCount = 0;53static int g_mockIoctlCallCount = 0;
48static uint32_t g_mockIoctlFailAfterCount = 0; // fail after N successful calls54static uint32_t g_mockIoctlFailAfterCount = 0; // fail after N successful calls
49static const void *g_lastIoctlData = nullptr;55static const void *g_lastIoctlData = nullptr;
56+static unsigned long g_lastIoctlReq = 0;
57+ 
58+static const int MAX_IOCTL_RECORDS = (MAX_POLICY_NUM + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
59+static uint32_t g_ioctlPathNums[MAX_IOCTL_RECORDS];
W
Wwangfenging12 天前

🤖 AI 代码检视意见 | ✨ Quality | ✅ Low | 行号区间: L58-L59

问题描述: g_ioctlPathNums数组容量MAX_IOCTL_RECORDS硬编码为64。当前MAX_POLICY_NUM=256对应批次数32,记录数足够。但若未来MAX_POLICY_NUM进一步增大(如512、1024),超出64批后超出部分的pathNum不会被记录,测试TC145等会因数组越界保护而静默跳过记录,测试可能仍通过但实际未验证。建议将MAX_IOCTL_RECORDS与MAX_POLICY_NUM关联以避免维护遗漏。

💡 查看修复建议(点击展开)

修改建议:将MAX_IOCTL_RECORDS与MAX_POLICY_NUM/KERNEL_BATCH_SIZE关联,确保记录容量永远足够。

static const int MAX_IOCTL_RECORDS = (MAX_POLICY_NUM + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
static uint32_t g_ioctlPathNums[MAX_IOCTL_RECORDS];
likedislike
50 60 
51static int g_mockOpenReturn = MOCK_OPEN_RETURN_FD;61static int g_mockOpenReturn = MOCK_OPEN_RETURN_FD;
52static const char *MOCK_OPEN_PATH = nullptr;62static const char *MOCK_OPEN_PATH = nullptr;
@@ -54,8 +64,33 @@ static const char *MOCK_OPEN_PATH = nullptr;
54static int g_mockClockTimeSec = MOCK_CLOCK_TIME_SEC;64static int g_mockClockTimeSec = MOCK_CLOCK_TIME_SEC;
55static int g_mockClockTimeNsec = MOCK_CLOCK_TIME_NSEC;65static int g_mockClockTimeNsec = MOCK_CLOCK_TIME_NSEC;
56 66 
67+static bool g_mockStrdupShouldFail = false;
68+ 
57// ==================== Wrap functions ====================69// ==================== Wrap functions ====================
58 70 
71+// Mirror of IoctlDecPolicyBatch in sandbox_dec.c for reading pathNum in mock ioctl
72+struct MockIoctlBatch {
73+ uint64_t tokenId;
74+ uint64_t timestamp;
75+ PathInfo path[KERNEL_BATCH_SIZE];
76+ uint32_t pathNum;
77+ int32_t userId;
78+ uint64_t reserved[DEC_POLICY_HEADER_RESERVED];
79+ bool flag;
80+};
81+ 
82+// Mirror of batch ioctl commands in sandbox_dec.c (based on MockIoctlBatch layout)
83+#define MOCK_SET_DEC_POLICY_CMD _IOWR(HM_DEC_IOCTL_BASE, HM_SET_POLICY_ID, MockIoctlBatch)
84+#define MOCK_CONSTRAINT_DEC_POLICY_CMD _IOW(HM_DEC_IOCTL_BASE, HM_CONSTRAINT_POLICY_ID, MockIoctlBatch)
85+#define MOCK_SET_DEC_PREFIX_CMD _IOWR(HM_DEC_IOCTL_BASE, HM_SET_PREFIX_ID, MockIoctlBatch)
86+#define MOCK_SET_DEC_IGNORE_CASE_CMD _IOWR(HM_DEC_IOCTL_BASE, HM_SET_DEC_IGNORE_CASE_ID, MockIoctlBatch)
87+ 
88+static bool IsBatchIoctlCmd(unsigned long request)
89+{
90+ return request == MOCK_SET_DEC_POLICY_CMD || request == MOCK_CONSTRAINT_DEC_POLICY_CMD ||
91+ request == MOCK_SET_DEC_PREFIX_CMD || request == MOCK_SET_DEC_IGNORE_CASE_CMD;
92+}
93+ 
59extern "C" {94extern "C" {
60int __wrap_open(const char *pathname, int flags, ...)95int __wrap_open(const char *pathname, int flags, ...)
61{96{
@@ -77,16 +112,18 @@ int __wrap_AddServerStageHook(AppSpawnHookStage stage, int prio, ServerStageHook
77int __wrap_ioctl(int fd, unsigned long request, ...)112int __wrap_ioctl(int fd, unsigned long request, ...)
78{113{
79 g_mockIoctlCallCount++;114 g_mockIoctlCallCount++;
115+ g_lastIoctlReq = request;
80 va_list args;116 va_list args;
81 va_start(args, request);117 va_start(args, request);
82 void *arg = va_arg(args, void *);118 void *arg = va_arg(args, void *);
83 va_end(args);119 va_end(args);
84- 
85- if (g_lastIoctlData != nullptr) {
86- g_lastIoctlData = arg;
87- }
88 g_lastIoctlData = arg;120 g_lastIoctlData = arg;
89 121 
122+ if (g_mockIoctlCallCount <= MAX_IOCTL_RECORDS && arg != nullptr && IsBatchIoctlCmd(request)) {
123+ MockIoctlBatch *batch = reinterpret_cast<MockIoctlBatch *>(arg);
124+ g_ioctlPathNums[g_mockIoctlCallCount - 1] = batch->pathNum;
125+ }
W
Wwangfenging12 天前

🤖 AI 代码检视意见 | 🛡️ Reliability | ✅ Low | 行号区间: L110-L113

问题描述: 在__wrap_ioctl中,对所有ioctl调用均假设arg指向MockIoctlBatch结构并读取pathNum字段。若被测代码未来调用其他ioctl命令(如DESTORY_DEC_POLICY_CMD使用uint64_t参数),将uint64_t缓冲指针强转为MockIoctlBatch*并读取pathNum属于类型混淆,虽然不会崩溃但记录的pathNum值无意义。当前测试覆盖的命令均为batch类型所以未暴露,但缺乏防御性。

💡 查看修复建议(点击展开)

修改建议:通过request参数判断是否为batch型ioctl命令,仅对batch命令记录pathNum,避免对非batch命令做类型强转读取。

if (g_mockIoctlCallCount <= MAX_IOCTL_RECORDS && arg != nullptr &&
(request == SET_DEC_POLICY_CMD || request == CONSTRAINT_DEC_POLICY_CMD ||
request == SET_DEC_PREFIX_CMD || request == SET_DEC_IGNORE_CASE_CMD)) {
MockIoctlBatch *batch = reinterpret_cast<MockIoctlBatch *>(arg);
likedislike
126+ 
90 if (g_mockIoctlFailAfterCount > 0 && g_mockIoctlCallCount > g_mockIoctlFailAfterCount) {127 if (g_mockIoctlFailAfterCount > 0 && g_mockIoctlCallCount > g_mockIoctlFailAfterCount) {
91 errno = EINVAL;128 errno = EINVAL;
92 return -1;129 return -1;
@@ -102,6 +139,16 @@ int __wrap_clock_gettime(clockid_t clk_id, struct timespec *ts)
102 }139 }
103 return 0;140 return 0;
104}141}
142+ 
143+char *__real_strdup(const char *s);
144+char *__wrap_strdup(const char *s)
145+{
146+ if (g_mockStrdupShouldFail) {
147+ errno = ENOMEM;
148+ return nullptr;
149+ }
150+ return __real_strdup(s);
151+}
105}152}
106 153 
107// ==================== Test helpers ====================154// ==================== Test helpers ====================
@@ -130,13 +177,18 @@ public:
130 g_mockIoctlCallCount = 0;177 g_mockIoctlCallCount = 0;
131 g_mockIoctlFailAfterCount = 0;178 g_mockIoctlFailAfterCount = 0;
132 g_lastIoctlData = nullptr;179 g_lastIoctlData = nullptr;
180+ g_lastIoctlReq = 0;
133 g_mockOpenReturn = MOCK_OPEN_RETURN_FD;181 g_mockOpenReturn = MOCK_OPEN_RETURN_FD;
134 MOCK_OPEN_PATH = nullptr;182 MOCK_OPEN_PATH = nullptr;
135 g_mockClockTimeSec = MOCK_CLOCK_TIME_SEC;183 g_mockClockTimeSec = MOCK_CLOCK_TIME_SEC;
136 g_mockClockTimeNsec = MOCK_CLOCK_TIME_NSEC;184 g_mockClockTimeNsec = MOCK_CLOCK_TIME_NSEC;
185+ g_mockStrdupShouldFail = false;
186+ for (int i = 0; i < MAX_IOCTL_RECORDS; i++) {
187+ g_ioctlPathNums[i] = 0;
188+ }
137 }189 }
138 190 
139- // Helper: create DecPolicyInfo with N paths (max KERNEL_BATCH_SIZE per call)191+ // Helper: create DecPolicyInfo with N paths (up to MAX_CONFIG_POLICY_NUM per call)
140 // pathOffset: starting index for path naming (for multi-batch tests)192 // pathOffset: starting index for path naming (for multi-batch tests)
141 void FillPolicyInfo(DecPolicyInfo &info, uint32_t pathCount, uint32_t pathOffset = 0)193 void FillPolicyInfo(DecPolicyInfo &info, uint32_t pathCount, uint32_t pathOffset = 0)
142 {194 {
@@ -144,9 +196,9 @@ public:
144 if (ret != EOK) {196 if (ret != EOK) {
145 return;197 return;
146 }198 }
147- // Limit to KERNEL_BATCH_SIZE to avoid buffer overflow199+ // Limit to MAX_CONFIG_POLICY_NUM (DecPolicyInfo.path[] capacity)
148- if (pathCount > KERNEL_BATCH_SIZE) {200+ if (pathCount > MAX_CONFIG_POLICY_NUM) {
149- pathCount = KERNEL_BATCH_SIZE;201+ pathCount = MAX_CONFIG_POLICY_NUM;
150 }202 }
151 info.tokenId = MOCK_TOKEN_ID;203 info.tokenId = MOCK_TOKEN_ID;
152 info.pathNum = pathCount;204 info.pathNum = pathCount;
@@ -240,18 +292,15 @@ HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicy_16Paths_2Batches_001, TestSize.
240 * @tc.desc: Verify SetDecPolicy delivers 64 paths in 8 batches (MAX_POLICY_NUM)292 * @tc.desc: Verify SetDecPolicy delivers 64 paths in 8 batches (MAX_POLICY_NUM)
241 * @tc.type: FUNC293 * @tc.type: FUNC
242 */294 */
243-HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicy_64Paths_8Batches_001, TestSize.Level1)295+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicy_MaxPaths_Batches_001, TestSize.Level1)
244{296{
245- // Use FillMultiplePolicyInfos to add 64 paths in 8 batches
246 FillMultiplePolicyInfos(MAX_POLICY_NUM);297 FillMultiplePolicyInfos(MAX_POLICY_NUM);
247 298 
248 g_mockIoctlCallCount = 0;299 g_mockIoctlCallCount = 0;
249 SetDecPolicy();300 SetDecPolicy();
250 301 
251- // Expect 8 ioctl calls (64 / 8 = 8 batches)
252 uint32_t expectedBatches = (MAX_POLICY_NUM + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;302 uint32_t expectedBatches = (MAX_POLICY_NUM + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
253 EXPECT_EQ(g_mockIoctlCallCount, static_cast<int>(expectedBatches));303 EXPECT_EQ(g_mockIoctlCallCount, static_cast<int>(expectedBatches));
254- EXPECT_EQ(expectedBatches, 8u);
255}304}
256 305 
257// ==================== TC104: Timestamp consistency ====================306// ==================== TC104: Timestamp consistency ====================
@@ -382,7 +431,7 @@ HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyInfos_NormalAdd_001, TestSize.Le
382 431 
383/**432/**
384 * @tc.name: SandboxDec_SetDecPolicyInfos_ExceedLimit_001433 * @tc.name: SandboxDec_SetDecPolicyInfos_ExceedLimit_001
385- * @tc.desc: Verify SetDecPolicyInfos rejects paths exceeding MAX_POLICY_NUM434+ * @tc.desc: Verify SetDecPolicyInfos keeps existing policies when exceeding MAX_POLICY_NUM
386 * @tc.type: FUNC435 * @tc.type: FUNC
387 */436 */
388HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyInfos_ExceedLimit_001, TestSize.Level1)437HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyInfos_ExceedLimit_001, TestSize.Level1)
@@ -390,15 +439,16 @@ HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyInfos_ExceedLimit_001, TestSize.
390 // First add MAX_POLICY_NUM paths using FillMultiplePolicyInfos439 // First add MAX_POLICY_NUM paths using FillMultiplePolicyInfos
391 FillMultiplePolicyInfos(MAX_POLICY_NUM);440 FillMultiplePolicyInfos(MAX_POLICY_NUM);
392 441 
393- // Try to add 1 more path - should be rejected and g_decPolicyInfos destroyed442+ // Try to add 1 more path - should be skipped (full), existing 64 retained
394 DecPolicyInfo infoExtra;443 DecPolicyInfo infoExtra;
395 FillPolicyInfo(infoExtra, 1);444 FillPolicyInfo(infoExtra, 1);
396 SetDecPolicyInfos(&infoExtra);445 SetDecPolicyInfos(&infoExtra);
397 446 
398- // SetDecPolicy should not call ioctl since g_decPolicyInfos was destroyed447+ // SetDecPolicy should still call ioctl for the retained paths
399 g_mockIoctlCallCount = 0;448 g_mockIoctlCallCount = 0;
400 SetDecPolicy();449 SetDecPolicy();
401- EXPECT_EQ(g_mockIoctlCallCount, 0);450+ uint32_t expectedBatches = (MAX_POLICY_NUM + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
451+ EXPECT_EQ(g_mockIoctlCallCount, static_cast<int>(expectedBatches));
402 452 
403 FreePolicyInfo(infoExtra);453 FreePolicyInfo(infoExtra);
404}454}
@@ -439,18 +489,24 @@ HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyBatch_InvalidParams_001, TestSiz
439 */489 */
440HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyInfos_ZeroPathNum_001, TestSize.Level1)490HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyInfos_ZeroPathNum_001, TestSize.Level1)
441{491{
492+ // Pre-fill 8 paths, then zero pathNum input must not touch existing g_decPolicyInfos
493+ DecPolicyInfo pre;
494+ FillPolicyInfo(pre, KERNEL_BATCH_SIZE);
495+ SetDecPolicyInfos(&pre);
496+ FreePolicyInfo(pre);
497+ 
442 DecPolicyInfo info;498 DecPolicyInfo info;
443 errno_t rc = memset_s(&info, sizeof(DecPolicyInfo), 0, sizeof(DecPolicyInfo));499 errno_t rc = memset_s(&info, sizeof(DecPolicyInfo), 0, sizeof(DecPolicyInfo));
444 ASSERT_EQ(rc, EOK);500 ASSERT_EQ(rc, EOK);
445 info.pathNum = 0;501 info.pathNum = 0;
446 502 
447- // Should not crash and not store anything
448 SetDecPolicyInfos(&info);503 SetDecPolicyInfos(&info);
449 504 
450- // SetDecPolicy should not call ioctl since no policies were stored505+ // Existing 8 paths retained: SetDecPolicy still delivers 1 batch
451 g_mockIoctlCallCount = 0;506 g_mockIoctlCallCount = 0;
452 SetDecPolicy();507 SetDecPolicy();
453- EXPECT_EQ(g_mockIoctlCallCount, 0);508+ EXPECT_EQ(g_mockIoctlCallCount, 1);
509+ EXPECT_EQ(g_ioctlPathNums[0], KERNEL_BATCH_SIZE);
454}510}
455 511 
456// ==================== TC114: 1 path, 1 batch ====================512// ==================== TC114: 1 path, 1 batch ====================
@@ -495,4 +551,907 @@ HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicy_9Paths_2Batches_001, TestSize.L
495 EXPECT_EQ(g_mockIoctlCallCount, 2);551 EXPECT_EQ(g_mockIoctlCallCount, 2);
496}552}
497 553 
554+// ==================== TC116: 32 paths in single call, 4 batches ====================
555+ 
556+/**
557+* @tc.name: SandboxDec_SetDecPolicy_32Paths_SingleCall_001
558+* @tc.desc: SetDecPolicyInfos accepts 32 paths in one call, delivers 4 batches
559+* @tc.type: FUNC
560+*/
561+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicy_32Paths_SingleCall_001, TestSize.Level1)
562+{
563+ DecPolicyInfo info;
564+ FillPolicyInfo(info, MAX_CONFIG_POLICY_NUM);
565+ 
566+ SetDecPolicyInfos(&info);
567+ 
568+ g_mockIoctlCallCount = 0;
569+ SetDecPolicy();
570+ 
571+ // 32 paths: 4 batches of 8
572+ uint32_t expectedBatches = (MAX_CONFIG_POLICY_NUM + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
573+ EXPECT_EQ(g_mockIoctlCallCount, static_cast<int>(expectedBatches));
574+ EXPECT_EQ(expectedBatches, 4u);
575+ 
576+ FreePolicyInfo(info);
577+}
578+ 
579+// ==================== TC117: exceed MAX_POLICY_NUM (global overflow) ====================
580+ 
581+/**
582+* @tc.name: SandboxDec_SetDecPolicyInfos_ExceedGlobalLimit_001
583+* @tc.desc: Verify SetDecPolicyInfos partially applies when global pathNum would exceed MAX_POLICY_NUM
584+* @tc.type: FUNC
585+*/
586+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyInfos_ExceedGlobalLimit_001, TestSize.Level1)
587+{
588+ // Fill global to MAX_POLICY_NUM - 1
589+ FillMultiplePolicyInfos(MAX_POLICY_NUM - 1);
590+ 
591+ // Add MAX_CONFIG_POLICY_NUM more => only 1 fits (64-63=1), rest skipped
592+ DecPolicyInfo info;
593+ FillPolicyInfo(info, MAX_CONFIG_POLICY_NUM);
594+ 
595+ SetDecPolicyInfos(&info);
596+ 
597+ // g_decPolicyInfos retained at MAX_POLICY_NUM, SetDecPolicy calls expected batches
598+ g_mockIoctlCallCount = 0;
599+ SetDecPolicy();
600+ uint32_t expectedBatches = (MAX_POLICY_NUM + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
601+ EXPECT_EQ(g_mockIoctlCallCount, static_cast<int>(expectedBatches));
602+ 
603+ FreePolicyInfo(info);
604+}
605+ 
606+// ==================== TC118: DestroyDecPolicyInfos(nullptr) ====================
607+ 
608+/**
609+ * @tc.name: SandboxDec_DestroyDecPolicyInfos_Null_001
610+ * @tc.desc: Verify DestroyDecPolicyInfos handles NULL input without crash
611+ * @tc.type: FUNC
612+ */
613+HWTEST_F(SandboxDecTest, SandboxDec_DestroyDecPolicyInfos_Null_001, TestSize.Level1)
614+{
615+ // NULL input: early return, no crash, no state change
616+ DestroyDecPolicyInfos(nullptr);
617+ EXPECT_EQ(g_mockIoctlCallCount, 0);
618+ 
619+ // Fill 8 paths, then destroy valid g_decPolicyInfos: global state cleared
620+ DecPolicyInfo info;
621+ FillPolicyInfo(info, KERNEL_BATCH_SIZE);
622+ SetDecPolicyInfos(&info);
623+ FreePolicyInfo(info);
624+ 
625+ SetDecPolicy(); // consumes and destroys g_decPolicyInfos internally
626+ g_mockIoctlCallCount = 0;
627+ 
628+ // After destroy, SetDecPolicy early-returns: no ioctl
629+ SetDecPolicy();
630+ EXPECT_EQ(g_mockIoctlCallCount, 0);
631+}
632+ 
633+// ==================== TC119: SetDecPolicyInfos(nullptr) ====================
634+ 
635+/**
636+ * @tc.name: SandboxDec_SetDecPolicyInfos_Null_001
637+ * @tc.desc: Verify SetDecPolicyInfos handles NULL input and SetDecPolicy does nothing
638+ * @tc.type: FUNC
639+ */
640+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyInfos_Null_001, TestSize.Level1)
641+{
642+ // Pre-fill 8 paths, then NULL input must not touch existing g_decPolicyInfos
643+ DecPolicyInfo pre;
644+ FillPolicyInfo(pre, KERNEL_BATCH_SIZE);
645+ SetDecPolicyInfos(&pre);
646+ FreePolicyInfo(pre);
647+ 
648+ SetDecPolicyInfos(nullptr);
649+ 
650+ // Existing 8 paths retained: SetDecPolicy still delivers 1 batch
651+ g_mockIoctlCallCount = 0;
652+ SetDecPolicy();
653+ EXPECT_EQ(g_mockIoctlCallCount, 1);
654+ EXPECT_EQ(g_ioctlPathNums[0], KERNEL_BATCH_SIZE);
655+}
656+ 
657+// ==================== TC120: SetDecPolicyInfos path[i].path==NULL ====================
658+ 
659+/**
660+ * @tc.name: SandboxDec_SetDecPolicyInfos_NullPath_001
661+ * @tc.desc: Verify SetDecPolicyInfos skips NULL path and continues
662+ * @tc.type: FUNC
663+ */
664+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyInfos_NullPath_001, TestSize.Level1)
665+{
666+ DecPolicyInfo info;
667+ errno_t rc = memset_s(&info, sizeof(DecPolicyInfo), 0, sizeof(DecPolicyInfo));
668+ ASSERT_EQ(rc, EOK);
669+ info.pathNum = 2;
670+ info.path[0].path = nullptr;
671+ info.path[1].path = strdup("/data/test/null_path_1");
672+ 
673+ SetDecPolicyInfos(&info);
674+ 
675+ // path[0] NULL skipped, path[1] added => 1 path in g_decPolicyInfos
676+ g_mockIoctlCallCount = 0;
677+ SetDecPolicy();
678+ EXPECT_EQ(g_mockIoctlCallCount, 1);
679+ 
680+ free(info.path[1].path);
681+}
682+ 
683+// ==================== TC121: SetDenyConstraintDirs normal ====================
684+ 
685+/**
686+ * @tc.name: SandboxDec_SetDenyConstraintDirs_Normal_001
687+ * @tc.desc: Verify SetDenyConstraintDirs delivers 8 constraint dirs in 1 batch
688+ * @tc.type: FUNC
689+ */
690+HWTEST_F(SandboxDecTest, SandboxDec_SetDenyConstraintDirs_Normal_001, TestSize.Level1)
691+{
692+ g_mockIoctlCallCount = 0;
693+ int ret = SetDenyConstraintDirs(nullptr);
694+ EXPECT_EQ(ret, 0);
695+ EXPECT_EQ(g_mockIoctlCallCount, 1);
696+ // g_decConstraintDir has exactly KERNEL_BATCH_SIZE entries: no end>size clip, full batch
697+ EXPECT_EQ(g_ioctlPathNums[0], KERNEL_BATCH_SIZE);
698+ EXPECT_NE(MOCK_OPEN_PATH, nullptr);
699+}
700+ 
701+// ==================== TC122: SetDenyConstraintDirs open fail ====================
702+ 
703+/**
704+ * @tc.name: SandboxDec_SetDenyConstraintDirs_OpenFail_001
705+ * @tc.desc: Verify SetDenyConstraintDirs handles open failure gracefully
706+ * @tc.type: FUNC
707+ */
708+HWTEST_F(SandboxDecTest, SandboxDec_SetDenyConstraintDirs_OpenFail_001, TestSize.Level1)
709+{
710+ g_mockOpenReturn = -1;
711+ g_mockIoctlCallCount = 0;
712+ 
713+ int ret = SetDenyConstraintDirs(nullptr);
714+ EXPECT_EQ(ret, 0);
715+ EXPECT_EQ(g_mockIoctlCallCount, 0);
716+}
717+ 
718+// ==================== TC123: SetDenyConstraintDirs ioctl fail ====================
719+ 
720+/**
721+ * @tc.name: SandboxDec_SetDenyConstraintDirs_IoctlFail_001
722+ * @tc.desc: Verify SetDenyConstraintDirs fail-open when ioctl fails
723+ * @tc.type: FUNC
724+ */
725+HWTEST_F(SandboxDecTest, SandboxDec_SetDenyConstraintDirs_IoctlFail_001, TestSize.Level1)
726+{
727+ g_mockIoctlReturn = -1;
728+ g_mockIoctlCallCount = 0;
729+ 
730+ int ret = SetDenyConstraintDirs(nullptr);
731+ EXPECT_EQ(ret, 0);
732+ EXPECT_EQ(g_mockIoctlCallCount, 1);
733+}
734+ 
735+// ==================== TC124: SetForcedPrefixDirs normal ====================
736+ 
737+/**
738+ * @tc.name: SandboxDec_SetForcedPrefixDirs_Normal_001
739+ * @tc.desc: Verify SetForcedPrefixDirs delivers 1 prefix dir in 1 batch
740+ * @tc.type: FUNC
741+ */
742+HWTEST_F(SandboxDecTest, SandboxDec_SetForcedPrefixDirs_Normal_001, TestSize.Level1)
743+{
744+ g_mockIoctlCallCount = 0;
745+ int ret = SetForcedPrefixDirs(nullptr);
746+ EXPECT_EQ(ret, 0);
747+ EXPECT_EQ(g_mockIoctlCallCount, 1);
748+ // g_decForcedPrefix has 1 entry: end>size clip triggers, partial batch with pathNum=1
749+ EXPECT_EQ(g_ioctlPathNums[0], 1u);
750+ EXPECT_NE(MOCK_OPEN_PATH, nullptr);
751+}
752+ 
753+// ==================== TC125: SetForcedPrefixDirs open fail ====================
754+ 
755+/**
756+ * @tc.name: SandboxDec_SetForcedPrefixDirs_OpenFail_001
757+ * @tc.desc: Verify SetForcedPrefixDirs handles open failure gracefully
758+ * @tc.type: FUNC
759+ */
760+HWTEST_F(SandboxDecTest, SandboxDec_SetForcedPrefixDirs_OpenFail_001, TestSize.Level1)
761+{
762+ g_mockOpenReturn = -1;
763+ g_mockIoctlCallCount = 0;
764+ 
765+ int ret = SetForcedPrefixDirs(nullptr);
766+ EXPECT_EQ(ret, 0);
767+ EXPECT_EQ(g_mockIoctlCallCount, 0);
768+}
769+ 
770+// ==================== TC126: SetDecPolicyBatch direct valid call ====================
771+ 
772+/**
773+ * @tc.name: SandboxDec_SetDecPolicyBatch_Valid_001
774+ * @tc.desc: Verify SetDecPolicyBatch succeeds with valid parameters
775+ * @tc.type: FUNC
776+ */
777+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyBatch_Valid_001, TestSize.Level1)
778+{
779+ GlobalDecPolicyInfo info;
780+ errno_t rc = memset_s(&info, sizeof(GlobalDecPolicyInfo), 0, sizeof(GlobalDecPolicyInfo));
781+ ASSERT_EQ(rc, EOK);
782+ info.pathNum = 4;
783+ info.path[0].path = (char *)"/data/test/batch0";
784+ info.path[0].pathLen = static_cast<uint32_t>(strlen("/data/test/batch0"));
785+ info.path[1].path = (char *)"/data/test/batch1";
786+ info.path[1].pathLen = static_cast<uint32_t>(strlen("/data/test/batch1"));
787+ info.path[2].path = (char *)"/data/test/batch2";
788+ info.path[2].pathLen = static_cast<uint32_t>(strlen("/data/test/batch2"));
789+ info.path[3].path = (char *)"/data/test/batch3";
790+ info.path[3].pathLen = static_cast<uint32_t>(strlen("/data/test/batch3"));
791+ 
792+ g_mockIoctlCallCount = 0;
793+ int ret = SetDecPolicyBatch(3, &info, 1000, 0, 4);
794+ EXPECT_EQ(ret, 0);
795+ EXPECT_EQ(g_mockIoctlCallCount, 1);
796+}
797+ 
798+// ==================== TC127: SetDecPolicyBatch direct ioctl fail ====================
799+ 
800+/**
801+ * @tc.name: SandboxDec_SetDecPolicyBatch_IoctlFail_001
802+ * @tc.desc: Verify SetDecPolicyBatch returns negative when ioctl fails
803+ * @tc.type: FUNC
804+ */
805+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyBatch_IoctlFail_001, TestSize.Level1)
806+{
807+ GlobalDecPolicyInfo info;
808+ errno_t rc = memset_s(&info, sizeof(GlobalDecPolicyInfo), 0, sizeof(GlobalDecPolicyInfo));
809+ ASSERT_EQ(rc, EOK);
810+ info.pathNum = 2;
811+ info.path[0].path = (char *)"/data/test/fail0";
812+ info.path[0].pathLen = static_cast<uint32_t>(strlen("/data/test/fail0"));
813+ info.path[1].path = (char *)"/data/test/fail1";
814+ info.path[1].pathLen = static_cast<uint32_t>(strlen("/data/test/fail1"));
815+ 
816+ g_mockIoctlReturn = -1;
817+ g_mockIoctlCallCount = 0;
818+ int ret = SetDecPolicyBatch(3, &info, 2000, 0, 2);
819+ EXPECT_LT(ret, 0);
820+ EXPECT_EQ(g_mockIoctlCallCount, 1);
821+}
822+ 
823+// ==================== TC128: SetDecPolicyBatch count == KERNEL_BATCH_SIZE (boundary pass) ====================
824+ 
825+/**
826+* @tc.name: SandboxDec_SetDecPolicyBatch_MaxBatchSize_001
827+* @tc.desc: Verify SetDecPolicyBatch succeeds when count == KERNEL_BATCH_SIZE (boundary)
828+* @tc.type: FUNC
829+*/
830+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyBatch_MaxBatchSize_001, TestSize.Level1)
831+{
832+ GlobalDecPolicyInfo info;
833+ errno_t rc = memset_s(&info, sizeof(GlobalDecPolicyInfo), 0, sizeof(GlobalDecPolicyInfo));
834+ ASSERT_EQ(rc, EOK);
835+ info.pathNum = KERNEL_BATCH_SIZE;
836+ for (uint32_t i = 0; i < KERNEL_BATCH_SIZE; i++) {
837+ info.path[i].path = (char *)"/data/test/batch_max";
838+ info.path[i].pathLen = static_cast<uint32_t>(strlen("/data/test/batch_max"));
839+ info.path[i].mode = 0x1;
840+ info.path[i].flag = false;
841+ }
842+ 
843+ g_mockIoctlCallCount = 0;
844+ int ret = SetDecPolicyBatch(3, &info, 1000, 0, KERNEL_BATCH_SIZE);
845+ EXPECT_EQ(ret, 0);
846+ EXPECT_EQ(g_mockIoctlCallCount, 1);
847+}
848+ 
849+// ==================== TC129: accumulate to exactly MAX_POLICY_NUM ====================
850+ 
851+/**
852+* @tc.name: SandboxDec_SetDecPolicyInfos_ExactlyMax_001
853+* @tc.desc: Verify SetDecPolicyInfos succeeds when total pathNum == MAX_POLICY_NUM (boundary)
854+* @tc.type: FUNC
855+*/
856+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyInfos_ExactlyMax_001, TestSize.Level1)
857+{
858+ // Fill MAX_POLICY_NUM - KERNEL_BATCH_SIZE = 56 paths first
859+ FillMultiplePolicyInfos(MAX_POLICY_NUM - KERNEL_BATCH_SIZE);
860+ 
861+ // Add KERNEL_BATCH_SIZE = 8 more => total = 64 = MAX_POLICY_NUM (boundary, should pass)
862+ DecPolicyInfo info;
863+ FillPolicyInfo(info, KERNEL_BATCH_SIZE);
864+ SetDecPolicyInfos(&info);
865+ 
866+ g_mockIoctlCallCount = 0;
867+ SetDecPolicy();
868+ 
869+ uint32_t expectedBatches = (MAX_POLICY_NUM + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
870+ EXPECT_EQ(g_mockIoctlCallCount, static_cast<int>(expectedBatches));
871+ FreePolicyInfo(info);
872+}
873+ 
874+// ==================== TC130: SetDecPolicy 7 paths (non-multiple, end > pathNum, 1 batch) ====================
875+ 
876+/**
877+* @tc.name: SandboxDec_SetDecPolicy_7Paths_1Batch_001
878+* @tc.desc: Verify SetDecPolicy delivers 7 paths in 1 batch (non-multiple triggers end>pathNum clip)
879+* @tc.type: FUNC
880+*/
881+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicy_7Paths_1Batch_001, TestSize.Level1)
882+{
883+ FillMultiplePolicyInfos(7);
884+ 
885+ g_mockIoctlCallCount = 0;
886+ SetDecPolicy();
887+ 
888+ // 7 paths: 1 batch, end=8>7 triggers clip to end=7
889+ EXPECT_EQ(g_mockIoctlCallCount, 1);
890+}
891+ 
892+// ==================== TC131: 17 paths, 3 batches, end>pathNum clip ====================
893+ 
894+/**
895+* @tc.name: SandboxDec_SetDecPolicy_17Paths_3Batches_001
896+* @tc.desc: Verify SetDecPolicy delivers 17 paths in 3 batches (end>pathNum clip on last batch)
897+* @tc.type: FUNC
898+*/
899+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicy_17Paths_3Batches_001, TestSize.Level1)
900+{
901+ FillMultiplePolicyInfos(17);
902+ 
903+ g_mockIoctlCallCount = 0;
904+ SetDecPolicy();
905+ 
906+ // 17 paths: batch1=8, batch2=8, batch3=1 (end=24>17 clips to 17)
907+ EXPECT_EQ(g_mockIoctlCallCount, 3);
908+}
909+ 
910+// ==================== TC132: SetIgnoreCaseDirs normal (AppSpawnMode) ====================
911+ 
912+/**
913+* @tc.name: SandboxDec_SetIgnoreCaseDirs_Normal_001
914+* @tc.desc: Verify SetIgnoreCaseDirs delivers ignore-case dirs in AppSpawn mode
915+* @tc.type: FUNC
916+*/
917+HWTEST_F(SandboxDecTest, SandboxDec_SetIgnoreCaseDirs_Normal_001, TestSize.Level1)
918+{
919+ AppSpawnMgr *mgr = (AppSpawnMgr *)calloc(1, sizeof(AppSpawnMgr));
920+ ASSERT_NE(mgr, nullptr);
921+ mgr->content.mode = MODE_FOR_APP_SPAWN;
922+ 
923+ g_mockIoctlCallCount = 0;
924+ g_mockOpenReturn = MOCK_OPEN_RETURN_FD;
925+ int ret = SetIgnoreCaseDirs(mgr);
926+ 
927+ EXPECT_EQ(ret, 0);
928+ EXPECT_EQ(g_mockIoctlCallCount, 1);
929+ // ignore-case list has 2 or 3 entries (< KERNEL_BATCH_SIZE): end>pathNum clip triggers
930+ EXPECT_GT(g_ioctlPathNums[0], 0u);
931+ EXPECT_LT(g_ioctlPathNums[0], KERNEL_BATCH_SIZE);
932+ EXPECT_NE(MOCK_OPEN_PATH, nullptr);
933+ 
934+ free(mgr);
935+}
936+ 
937+// ==================== TC133: SetIgnoreCaseDirs wrong mode (early return) ====================
938+ 
939+/**
940+* @tc.name: SandboxDec_SetIgnoreCaseDirs_WrongMode_001
941+* @tc.desc: Verify SetIgnoreCaseDirs returns 0 early when not App/Native spawn mode
942+* @tc.type: FUNC
943+*/
944+HWTEST_F(SandboxDecTest, SandboxDec_SetIgnoreCaseDirs_WrongMode_001, TestSize.Level1)
945+{
946+ AppSpawnMgr *mgr = (AppSpawnMgr *)calloc(1, sizeof(AppSpawnMgr));
947+ ASSERT_NE(mgr, nullptr);
948+ mgr->content.mode = MODE_FOR_NWEB_SPAWN;
949+ 
950+ g_mockIoctlCallCount = 0;
951+ int ret = SetIgnoreCaseDirs(mgr);
952+ 
953+ EXPECT_EQ(ret, 0);
954+ EXPECT_EQ(g_mockIoctlCallCount, 0);
955+ 
956+ free(mgr);
957+}
958+ 
959+// ==================== TC134: SetIgnoreCaseDirs open fail ====================
960+ 
961+/**
962+* @tc.name: SandboxDec_SetIgnoreCaseDirs_OpenFail_001
963+* @tc.desc: Verify SetIgnoreCaseDirs handles open failure gracefully
964+* @tc.type: FUNC
965+*/
966+HWTEST_F(SandboxDecTest, SandboxDec_SetIgnoreCaseDirs_OpenFail_001, TestSize.Level1)
967+{
968+ AppSpawnMgr *mgr = (AppSpawnMgr *)calloc(1, sizeof(AppSpawnMgr));
969+ ASSERT_NE(mgr, nullptr);
970+ mgr->content.mode = MODE_FOR_NATIVE_SPAWN;
971+ 
972+ g_mockOpenReturn = -1;
973+ g_mockIoctlCallCount = 0;
974+ int ret = SetIgnoreCaseDirs(mgr);
975+ 
976+ EXPECT_EQ(ret, 0);
977+ EXPECT_EQ(g_mockIoctlCallCount, 0);
978+ 
979+ free(mgr);
980+}
981+ 
982+// ==================== TC135: SetDecPolicyInfos mid-loop NULL path (path[0] valid, path[1] NULL) ====================
983+ 
984+/**
985+* @tc.name: SandboxDec_SetDecPolicyInfos_MidLoopNullPath_001
986+* @tc.desc: Verify SetDecPolicyInfos keeps already-added path when NULL encountered at i=1
987+* @tc.type: FUNC
988+*/
989+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyInfos_MidLoopNullPath_001, TestSize.Level1)
990+{
991+ DecPolicyInfo info;
992+ errno_t rc = memset_s(&info, sizeof(DecPolicyInfo), 0, sizeof(DecPolicyInfo));
993+ ASSERT_EQ(rc, EOK);
994+ info.pathNum = 2;
995+ info.path[0].path = strdup("/data/test/midloop_valid");
996+ ASSERT_NE(info.path[0].path, nullptr);
997+ info.path[0].pathLen = static_cast<uint32_t>(strlen("/data/test/midloop_valid"));
998+ info.path[0].mode = 0x1;
999+ info.path[1].path = nullptr;
1000+ 
1001+ SetDecPolicyInfos(&info);
1002+ 
1003+ // path[0] added (pathNum=1), path[1] NULL skipped, g_decPolicyInfos retained
1004+ g_mockIoctlCallCount = 0;
1005+ SetDecPolicy();
1006+ EXPECT_EQ(g_mockIoctlCallCount, 1);
1007+ 
1008+ free(info.path[0].path);
1009+}
1010+ 
1011+// ==================== TC136: SetDenyConstraintDirs ioctl success path ====================
1012+ 
1013+/**
1014+* @tc.name: SandboxDec_SetDenyConstraintDirs_IoctlSuccess_001
1015+* @tc.desc: Verify SetDenyConstraintDirs executes ioctl success branch (ret >= 0)
1016+* @tc.type: FUNC
1017+*/
1018+HWTEST_F(SandboxDecTest, SandboxDec_SetDenyConstraintDirs_IoctlSuccess_001, TestSize.Level1)
1019+{
1020+ g_mockIoctlReturn = 0;
1021+ g_mockIoctlCallCount = 0;
1022+ g_lastIoctlReq = 0;
1023+ 
1024+ int ret = SetDenyConstraintDirs(nullptr);
1025+ 
1026+ EXPECT_EQ(ret, 0);
1027+ EXPECT_EQ(g_mockIoctlCallCount, 1);
1028+ EXPECT_NE(g_lastIoctlReq, 0UL);
1029+}
1030+ 
1031+// ==================== TC137: SetForcedPrefixDirs ioctl success path ====================
1032+ 
1033+/**
1034+* @tc.name: SandboxDec_SetForcedPrefixDirs_IoctlSuccess_001
1035+* @tc.desc: Verify SetForcedPrefixDirs executes ioctl success branch (ret >= 0)
1036+* @tc.type: FUNC
1037+*/
1038+HWTEST_F(SandboxDecTest, SandboxDec_SetForcedPrefixDirs_IoctlSuccess_001, TestSize.Level1)
1039+{
1040+ g_mockIoctlReturn = 0;
1041+ g_mockIoctlCallCount = 0;
1042+ g_lastIoctlReq = 0;
1043+ 
1044+ int ret = SetForcedPrefixDirs(nullptr);
1045+ 
1046+ EXPECT_EQ(ret, 0);
1047+ EXPECT_EQ(g_mockIoctlCallCount, 1);
1048+ EXPECT_NE(g_lastIoctlReq, 0UL);
1049+}
1050+ 
1051+// ==================== TC138: SetForcedPrefixDirs ioctl fail ====================
1052+ 
1053+/**
1054+* @tc.name: SandboxDec_SetForcedPrefixDirs_IoctlFail_001
1055+* @tc.desc: Verify SetForcedPrefixDirs handles ioctl failure gracefully (fail-open)
1056+* @tc.type: FUNC
1057+*/
1058+HWTEST_F(SandboxDecTest, SandboxDec_SetForcedPrefixDirs_IoctlFail_001, TestSize.Level1)
1059+{
1060+ g_mockIoctlReturn = -1;
1061+ g_mockIoctlCallCount = 0;
1062+ 
1063+ int ret = SetForcedPrefixDirs(nullptr);
1064+ 
1065+ EXPECT_EQ(ret, 0);
1066+ EXPECT_EQ(g_mockIoctlCallCount, 1);
1067+}
1068+ 
1069+// ==================== TC139: SetIgnoreCaseDirs ioctl fail ====================
1070+ 
1071+/**
1072+* @tc.name: SandboxDec_SetIgnoreCaseDirs_IoctlFail_001
1073+* @tc.desc: Verify SetIgnoreCaseDirs handles ioctl failure gracefully (fail-open)
1074+* @tc.type: FUNC
1075+*/
1076+HWTEST_F(SandboxDecTest, SandboxDec_SetIgnoreCaseDirs_IoctlFail_001, TestSize.Level1)
1077+{
1078+ AppSpawnMgr *mgr = (AppSpawnMgr *)calloc(1, sizeof(AppSpawnMgr));
1079+ ASSERT_NE(mgr, nullptr);
1080+ mgr->content.mode = MODE_FOR_APP_SPAWN;
1081+ 
1082+ g_mockIoctlReturn = -1;
1083+ g_mockIoctlCallCount = 0;
1084+ g_mockOpenReturn = MOCK_OPEN_RETURN_FD;
1085+ 
1086+ int ret = SetIgnoreCaseDirs(mgr);
1087+ 
1088+ EXPECT_EQ(ret, 0);
1089+ EXPECT_EQ(g_mockIoctlCallCount, 1);
1090+ 
1091+ free(mgr);
1092+}
1093+ 
1094+// ==================== TC140: SetDecPolicyBatch with non-zero start ====================
1095+ 
1096+/**
1097+* @tc.name: SandboxDec_SetDecPolicyBatch_NonZeroStart_001
1098+* @tc.desc: Verify SetDecPolicyBatch correctly indexes paths when start > 0
1099+* @tc.type: FUNC
1100+*/
1101+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyBatch_NonZeroStart_001, TestSize.Level1)
1102+{
1103+ GlobalDecPolicyInfo info;
1104+ errno_t rc = memset_s(&info, sizeof(GlobalDecPolicyInfo), 0, sizeof(GlobalDecPolicyInfo));
1105+ ASSERT_EQ(rc, EOK);
1106+ info.pathNum = KERNEL_BATCH_SIZE * 2; // 16 paths
1107+ for (uint32_t i = 0; i < KERNEL_BATCH_SIZE * 2; i++) {
1108+ info.path[i].path = (char *)"/data/test/start_offset";
1109+ info.path[i].pathLen = static_cast<uint32_t>(strlen("/data/test/start_offset"));
1110+ info.path[i].mode = 0x1;
1111+ info.path[i].flag = false;
1112+ }
1113+ 
1114+ // Batch 2: start=8, count=8
1115+ g_mockIoctlCallCount = 0;
1116+ int ret = SetDecPolicyBatch(3, &info, 5000, KERNEL_BATCH_SIZE, KERNEL_BATCH_SIZE);
1117+ EXPECT_EQ(ret, 0);
1118+ EXPECT_EQ(g_mockIoctlCallCount, 1);
1119+}
1120+ 
1121+// ==================== TC141: SetDecPolicyInfos strdup fail (skip path) ====================
1122+ 
1123+/**
1124+* @tc.name: SandboxDec_SetDecPolicyInfos_StrdupFail_001
1125+* @tc.desc: Verify SetDecPolicyInfos skips path when strdup fails, retains existing
1126+* @tc.type: FUNC
1127+*/
1128+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyInfos_StrdupFail_001, TestSize.Level1)
1129+{
1130+ // Pre-fill 8 paths successfully
1131+ DecPolicyInfo pre;
1132+ FillPolicyInfo(pre, KERNEL_BATCH_SIZE);
1133+ SetDecPolicyInfos(&pre);
1134+ FreePolicyInfo(pre);
1135+ 
1136+ // Then 2 more paths, but strdup fails for both: skipped, existing 8 retained
1137+ DecPolicyInfo info;
1138+ FillPolicyInfo(info, 2, KERNEL_BATCH_SIZE);
1139+ 
1140+ g_mockStrdupShouldFail = true;
1141+ SetDecPolicyInfos(&info);
1142+ g_mockStrdupShouldFail = false;
1143+ 
1144+ // Still exactly 8 paths = 1 batch
1145+ g_mockIoctlCallCount = 0;
1146+ SetDecPolicy();
1147+ EXPECT_EQ(g_mockIoctlCallCount, 1);
1148+ EXPECT_EQ(g_ioctlPathNums[0], KERNEL_BATCH_SIZE);
1149+ 
1150+ FreePolicyInfo(info);
1151+}
1152+ 
1153+// ==================== TC142: 32 paths single call, verify per-batch pathNum [8,8,8,8] ====================
1154+ 
1155+/**
1156+* @tc.name: SandboxDec_SetDecPolicy_32Paths_BatchPathNums_001
1157+* @tc.desc: Verify SetDecPolicyInfos stores exactly 32 paths, delivered as 4 batches each with pathNum=8
1158+* @tc.type: FUNC
1159+*/
1160+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicy_32Paths_BatchPathNums_001, TestSize.Level1)
1161+{
1162+ DecPolicyInfo info;
1163+ FillPolicyInfo(info, MAX_CONFIG_POLICY_NUM);
1164+ SetDecPolicyInfos(&info);
1165+ 
1166+ g_mockIoctlCallCount = 0;
1167+ SetDecPolicy();
1168+ 
1169+ EXPECT_EQ(g_mockIoctlCallCount, 4);
1170+ for (int i = 0; i < 4; i++) {
1171+ EXPECT_EQ(g_ioctlPathNums[i], KERNEL_BATCH_SIZE)
1172+ << "batch " << i << " pathNum mismatch";
1173+ }
1174+ FreePolicyInfo(info);
1175+}
1176+ 
1177+// ==================== TC143: 31 paths single call, verify per-batch pathNum [8,8,8,7] ====================
1178+ 
1179+/**
1180+* @tc.name: SandboxDec_SetDecPolicy_31Paths_BatchPathNums_001
1181+* @tc.desc: Verify 31 paths stored and delivered as 4 batches [8,8,8,7] (no truncation under MAX_CONFIG_POLICY_NUM)
1182+* @tc.type: FUNC
1183+*/
1184+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicy_31Paths_BatchPathNums_001, TestSize.Level1)
1185+{
1186+ DecPolicyInfo info;
1187+ FillPolicyInfo(info, MAX_CONFIG_POLICY_NUM - 1);
1188+ SetDecPolicyInfos(&info);
1189+ 
1190+ g_mockIoctlCallCount = 0;
1191+ SetDecPolicy();
1192+ 
1193+ EXPECT_EQ(g_mockIoctlCallCount, 4);
1194+ EXPECT_EQ(g_ioctlPathNums[0], KERNEL_BATCH_SIZE);
1195+ EXPECT_EQ(g_ioctlPathNums[1], KERNEL_BATCH_SIZE);
1196+ EXPECT_EQ(g_ioctlPathNums[2], KERNEL_BATCH_SIZE);
1197+ EXPECT_EQ(g_ioctlPathNums[3], MAX_CONFIG_POLICY_NUM - 1 - KERNEL_BATCH_SIZE * 3);
1198+ FreePolicyInfo(info);
1199+}
1200+ 
1201+// ==================== TC144: 33 paths (32+1), verify per-batch pathNum [8,8,8,8,1] ====================
1202+ 
1203+/**
1204+* @tc.name: SandboxDec_SetDecPolicy_33Paths_BatchPathNums_001
1205+* @tc.desc: Verify 33 paths (two SetDecPolicyInfos calls: 32+1) delivered as 5 batches [8,8,8,8,1]
1206+* @tc.type: FUNC
1207+*/
1208+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicy_33Paths_BatchPathNums_001, TestSize.Level1)
1209+{
1210+ DecPolicyInfo info32;
1211+ FillPolicyInfo(info32, MAX_CONFIG_POLICY_NUM);
1212+ SetDecPolicyInfos(&info32);
1213+ FreePolicyInfo(info32);
1214+ 
1215+ DecPolicyInfo info1;
1216+ FillPolicyInfo(info1, 1, MAX_CONFIG_POLICY_NUM);
1217+ SetDecPolicyInfos(&info1);
1218+ FreePolicyInfo(info1);
1219+ 
1220+ g_mockIoctlCallCount = 0;
1221+ SetDecPolicy();
1222+ 
1223+ EXPECT_EQ(g_mockIoctlCallCount, 5);
1224+ for (int i = 0; i < 4; i++) {
1225+ EXPECT_EQ(g_ioctlPathNums[i], KERNEL_BATCH_SIZE)
1226+ << "batch " << i << " pathNum mismatch";
1227+ }
1228+ EXPECT_EQ(g_ioctlPathNums[4], 1u);
1229+}
1230+ 
1231+// ==================== TC145: MAX_POLICY_NUM paths, verify per-batch pathNum ====================
1232+ 
1233+/**
1234+* @tc.name: SandboxDec_SetDecPolicy_MaxPaths_BatchPathNums_001
1235+* @tc.desc: Verify MAX_POLICY_NUM paths delivered as expected batches each with pathNum=KERNEL_BATCH_SIZE
1236+* @tc.type: FUNC
1237+*/
1238+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicy_MaxPaths_BatchPathNums_001, TestSize.Level1)
1239+{
1240+ FillMultiplePolicyInfos(MAX_POLICY_NUM);
1241+ 
1242+ g_mockIoctlCallCount = 0;
1243+ SetDecPolicy();
1244+ 
1245+ uint32_t expectedBatches = (MAX_POLICY_NUM + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
1246+ EXPECT_EQ(g_mockIoctlCallCount, static_cast<int>(expectedBatches));
1247+ for (uint32_t i = 0; i < expectedBatches; i++) {
1248+ EXPECT_EQ(g_ioctlPathNums[i], KERNEL_BATCH_SIZE)
1249+ << "batch " << i << " pathNum mismatch";
1250+ }
1251+}
1252+ 
1253+// ==================== TC146: 1 path, verify per-batch pathNum [1] ====================
1254+ 
1255+/**
1256+* @tc.name: SandboxDec_SetDecPolicy_1Path_BatchPathNum_001
1257+* @tc.desc: Verify single path delivered as 1 batch with pathNum=1
1258+* @tc.type: FUNC
1259+*/
1260+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicy_1Path_BatchPathNum_001, TestSize.Level1)
1261+{
1262+ DecPolicyInfo info;
1263+ FillPolicyInfo(info, 1);
1264+ SetDecPolicyInfos(&info);
1265+ 
1266+ g_mockIoctlCallCount = 0;
1267+ SetDecPolicy();
1268+ 
1269+ EXPECT_EQ(g_mockIoctlCallCount, 1);
1270+ EXPECT_EQ(g_ioctlPathNums[0], 1u);
1271+ FreePolicyInfo(info);
1272+}
1273+ 
1274+// ==================== TC147: 9 paths, verify per-batch pathNum [8,1] ====================
1275+ 
1276+/**
1277+* @tc.name: SandboxDec_SetDecPolicy_9Paths_BatchPathNums_001
1278+* @tc.desc: Verify 9 paths delivered as 2 batches [8,1]
1279+* @tc.type: FUNC
1280+*/
1281+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicy_9Paths_BatchPathNums_001, TestSize.Level1)
1282+{
1283+ DecPolicyInfo info9;
1284+ FillPolicyInfo(info9, KERNEL_BATCH_SIZE);
1285+ SetDecPolicyInfos(&info9);
1286+ FreePolicyInfo(info9);
1287+ 
1288+ DecPolicyInfo info1;
1289+ FillPolicyInfo(info1, 1, KERNEL_BATCH_SIZE);
1290+ SetDecPolicyInfos(&info1);
1291+ FreePolicyInfo(info1);
1292+ 
1293+ g_mockIoctlCallCount = 0;
1294+ SetDecPolicy();
1295+ 
1296+ EXPECT_EQ(g_mockIoctlCallCount, 2);
1297+ EXPECT_EQ(g_ioctlPathNums[0], KERNEL_BATCH_SIZE);
1298+ EXPECT_EQ(g_ioctlPathNums[1], 1u);
1299+}
1300+ 
1301+// ==================== TC148: fill to exactly MAX_POLICY_NUM, no second-layer clip ====================
1302+ 
1303+/**
1304+* @tc.name: SandboxDec_SetDecPolicy_FillExactlyFull_001
1305+* @tc.desc: Verify filling global to exactly MAX_POLICY_NUM, no second-layer clip
1306+* @tc.type: FUNC
1307+*/
1308+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicy_FillExactlyFull_001, TestSize.Level1)
1309+{
1310+ FillMultiplePolicyInfos(MAX_POLICY_NUM);
1311+ 
1312+ g_mockIoctlCallCount = 0;
1313+ SetDecPolicy();
1314+ 
1315+ uint32_t expectedBatches = (MAX_POLICY_NUM + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
1316+ EXPECT_EQ(g_mockIoctlCallCount, static_cast<int>(expectedBatches));
1317+ for (uint32_t i = 0; i < expectedBatches; i++) {
1318+ EXPECT_EQ(g_ioctlPathNums[i], KERNEL_BATCH_SIZE)
1319+ << "batch " << i << " pathNum mismatch";
1320+ }
1321+}
1322+ 
1323+// ==================== TC149: MAX_POLICY_NUM-1 + MAX_CONFIG_POLICY_NUM, second-layer clip ====================
1324+ 
1325+/**
1326+* @tc.name: SandboxDec_SetDecPolicy_SecondLayerClip_001
1327+* @tc.desc: Verify partial apply when global + new > MAX_POLICY_NUM (second-layer clip)
1328+* @tc.type: FUNC
1329+*/
1330+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicy_SecondLayerClip_001, TestSize.Level1)
1331+{
1332+ FillMultiplePolicyInfos(MAX_POLICY_NUM - 1);
1333+ 
1334+ DecPolicyInfo info;
1335+ FillPolicyInfo(info, MAX_CONFIG_POLICY_NUM, MAX_POLICY_NUM - 1);
1336+ SetDecPolicyInfos(&info);
1337+ FreePolicyInfo(info);
1338+ 
1339+ g_mockIoctlCallCount = 0;
1340+ SetDecPolicy();
1341+ 
1342+ uint32_t expectedBatches = (MAX_POLICY_NUM + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
1343+ EXPECT_EQ(g_mockIoctlCallCount, static_cast<int>(expectedBatches));
1344+ for (uint32_t i = 0; i < expectedBatches; i++) {
1345+ EXPECT_EQ(g_ioctlPathNums[i], KERNEL_BATCH_SIZE)
1346+ << "batch " << i << " pathNum mismatch";
1347+ }
1348+}
1349+ 
1350+// ==================== TC150: second-layer clip, verify batch distribution ====================
1351+ 
1352+/**
1353+* @tc.name: SandboxDec_SetDecPolicy_ClipBatchDistribution_001
1354+* @tc.desc: Verify after second-layer clip, paths distributed as full batches + 1 remainder
1355+* @tc.type: FUNC
1356+*/
1357+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicy_ClipBatchDistribution_001, TestSize.Level1)
1358+{
1359+ FillMultiplePolicyInfos(MAX_POLICY_NUM - 1);
1360+ DecPolicyInfo info;
1361+ FillPolicyInfo(info, MAX_CONFIG_POLICY_NUM, MAX_POLICY_NUM - 1);
1362+ SetDecPolicyInfos(&info);
1363+ FreePolicyInfo(info);
1364+ 
1365+ g_mockIoctlCallCount = 0;
1366+ SetDecPolicy();
1367+ 
1368+ uint32_t expectedBatches = (MAX_POLICY_NUM + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
1369+ EXPECT_EQ(g_mockIoctlCallCount, static_cast<int>(expectedBatches));
1370+ uint32_t totalPaths = 0;
1371+ for (uint32_t i = 0; i < expectedBatches; i++) {
1372+ EXPECT_EQ(g_ioctlPathNums[i], KERNEL_BATCH_SIZE)
1373+ << "batch " << i << " pathNum mismatch";
1374+ totalPaths += g_ioctlPathNums[i];
1375+ }
1376+ EXPECT_EQ(totalPaths, static_cast<uint32_t>(MAX_POLICY_NUM));
1377+}
1378+ 
1379+// ==================== TC151: SetDecPolicyInfos clamp pathNum > MAX_CONFIG_POLICY_NUM ====================
1380+ 
1381+/**
1382+* @tc.name: SandboxDec_SetDecPolicyInfos_InputClamp_001
1383+* @tc.desc: Verify SetDecPolicyInfos clamps input pathNum to MAX_CONFIG_POLICY_NUM, no OOB read
1384+* @tc.type: FUNC
1385+*/
1386+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyInfos_InputClamp_001, TestSize.Level1)
1387+{
1388+ DecPolicyInfo info;
1389+ errno_t rc = memset_s(&info, sizeof(DecPolicyInfo), 0, sizeof(DecPolicyInfo));
1390+ ASSERT_EQ(rc, EOK);
1391+ // Only fill real path entries (capacity is MAX_CONFIG_POLICY_NUM)
1392+ for (uint32_t i = 0; i < MAX_CONFIG_POLICY_NUM; i++) {
1393+ std::string pathStr = "/data/test/clamp_" + std::to_string(i);
1394+ info.path[i].path = strdup(pathStr.c_str());
1395+ info.path[i].pathLen = static_cast<uint32_t>(pathStr.length());
1396+ info.path[i].mode = 0x1;
1397+ }
1398+ // Lie about pathNum: claims more than path[] capacity
1399+ info.pathNum = MAX_CONFIG_POLICY_NUM + 8;
1400+ info.tokenId = MOCK_TOKEN_ID;
1401+ 
1402+ SetDecPolicyInfos(&info);
1403+ 
1404+ // Input clamped to MAX_CONFIG_POLICY_NUM, stored paths delivered as batches
1405+ g_mockIoctlCallCount = 0;
1406+ SetDecPolicy();
1407+ uint32_t expectedBatches = (MAX_CONFIG_POLICY_NUM + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
1408+ EXPECT_EQ(g_mockIoctlCallCount, static_cast<int>(expectedBatches));
1409+ uint32_t totalPaths = 0;
1410+ for (uint32_t i = 0; i < expectedBatches; i++) {
1411+ totalPaths += g_ioctlPathNums[i];
1412+ }
1413+ EXPECT_EQ(totalPaths, static_cast<uint32_t>(MAX_CONFIG_POLICY_NUM));
1414+ 
1415+ for (uint32_t i = 0; i < MAX_CONFIG_POLICY_NUM; i++) {
1416+ free(info.path[i].path);
1417+ }
1418+}
1419+ 
1420+// ==================== TC152: SetDecPolicyInfos input clamp preserves valid prefix ====================
1421+ 
1422+/**
1423+* @tc.name: SandboxDec_SetDecPolicyInfos_InputClampBoundary_001
1424+* @tc.desc: Verify input pathNum == MAX_CONFIG_POLICY_NUM + 1 clamps to MAX_CONFIG_POLICY_NUM
1425+* @tc.type: FUNC
1426+*/
1427+HWTEST_F(SandboxDecTest, SandboxDec_SetDecPolicyInfos_InputClampBoundary_001, TestSize.Level1)
1428+{
1429+ DecPolicyInfo info;
1430+ errno_t rc = memset_s(&info, sizeof(DecPolicyInfo), 0, sizeof(DecPolicyInfo));
1431+ ASSERT_EQ(rc, EOK);
1432+ for (uint32_t i = 0; i < MAX_CONFIG_POLICY_NUM; i++) {
1433+ std::string pathStr = "/data/test/clamp_b_" + std::to_string(i);
1434+ info.path[i].path = strdup(pathStr.c_str());
1435+ info.path[i].pathLen = static_cast<uint32_t>(pathStr.length());
1436+ info.path[i].mode = 0x1;
1437+ }
1438+ info.pathNum = MAX_CONFIG_POLICY_NUM + 1;
1439+ 
1440+ SetDecPolicyInfos(&info);
1441+ 
1442+ g_mockIoctlCallCount = 0;
1443+ SetDecPolicy();
1444+ uint32_t expectedBatches = (MAX_CONFIG_POLICY_NUM + KERNEL_BATCH_SIZE - 1) / KERNEL_BATCH_SIZE;
1445+ EXPECT_EQ(g_mockIoctlCallCount, static_cast<int>(expectedBatches));
1446+ uint32_t totalPaths = 0;
1447+ for (uint32_t i = 0; i < expectedBatches; i++) {
1448+ totalPaths += g_ioctlPathNums[i];
1449+ }
1450+ EXPECT_EQ(totalPaths, static_cast<uint32_t>(MAX_CONFIG_POLICY_NUM));
1451+ 
1452+ for (uint32_t i = 0; i < MAX_CONFIG_POLICY_NUM; i++) {
1453+ free(info.path[i].path);
1454+ }
1455+}
1456+ 
498} // namespace OHOS1457} // namespace OHOS