C语言mongoc BSON解析场景下结构体指针数组成员赋值问题求解
问题排查
- 野指针访问:代码中声明的
skill_data *parameterset未分配内存空间,就直接访问其成员写入数据,属于典型的未定义行为,是程序故障的核心原因。 - 类型判断逻辑错误:
if (bson_iter_type (&child3) == 0x01 || 0x10 || 0x12)写法不符合C语言语法规则,该表达式会永久返回真(因为0x10为非零常量,逻辑或运算只要有一个操作数非零即返回真),所有类型的字段都会进入数值赋值分支,字符串类型分支永远不会触发。 - 缺少接口返回值校验:所有
bson_iter_find、bson_iter_recurse的返回值都未做完整性校验,只要某一步查找失败,后续使用的迭代器就是非法状态,直接访问会触发内存错误。 - 数组越界风险:结构体中
key、literal、numeral的数组长度均为50,代码未对循环变量i做上限判断,当ParameterSet下的字段数超过50时会发生数组越界,破坏内存结构。 - 字符串溢出风险:直接使用
strcpy拷贝字符串,未判断源字符串长度是否超过目标数组(单个数组容量为50字节)的容量,长字符串会溢出导致内存污染。 - 错误场景无兜底处理:如果任意一步解析失败,返回的指针未做初始化处理,调用方无法判断解析是否成功。
修复后的参考代码
#include <stdlib.h> #include <string.h> #include <bson.h> typedef struct { char key [50][50]; char literal [50][50]; double numeral [50]; } skill_data; skill_data * mongoc_fetchSkillParameter (const bson_t *recipe, char *skill_id) { bson_iter_t iter; bson_iter_t child1; bson_iter_t child2; bson_iter_t child3; int i = 0; // 给结构体分配内存 skill_data *parameterset = malloc(sizeof(skill_data)); if (!parameterset) { return NULL; } // 初始化结构体内容为0 memset(parameterset, 0, sizeof(skill_data)); bson_iter_init(&iter, recipe); if (!bson_iter_find (&iter, "Skills") || !BSON_ITER_HOLDS_ARRAY (&iter) || !bson_iter_recurse (&iter, &child1)) { free(parameterset); return NULL; } // 注意:此处只取了Skills数组的第一个元素,如果你需要匹配skill_id需要补充遍历查找逻辑 if (!bson_iter_next (&child1) || !BSON_ITER_HOLDS_DOCUMENT (&child1) || !bson_iter_recurse (&child1, &child2)) { free(parameterset); return NULL; } if (!bson_iter_find(&child2, "ParameterSet") || !BSON_ITER_HOLDS_DOCUMENT (&child2) || !bson_iter_recurse (&child2, &child3)) { free(parameterset); return NULL; } i = 0; while (i < 50 && bson_iter_next (&child3)) { // 安全拷贝key,避免溢出 strncpy(parameterset->key[i], bson_iter_key (&child3), 49); parameterset->key[i][49] = '\0'; bson_type_t type = bson_iter_type(&child3); if (type == BSON_TYPE_DOUBLE || type == BSON_TYPE_INT32 || type == BSON_TYPE_INT64) { parameterset->numeral[i] = bson_iter_as_double (&child3); // 非字符串类型的literal默认留空即可,因为结构体已经memset为0 } else if (type == BSON_TYPE_UTF8) { const char *str_val = bson_iter_utf8 (&child3, NULL); strncpy(parameterset->literal[i], str_val, 49); parameterset->literal[i][49] = '\0'; } i++; } return parameterset; }
额外说明
- 代码中传入的
skill_id参数暂未使用,原逻辑只取了Skills数组的第一个元素,如果你需要根据skill_id匹配对应的技能项,需要补充遍历Skills数组的逻辑。 - 调用方使用完返回的
skill_data指针后,需要主动调用free释放内存,避免内存泄漏。
内容的提问来源于stack exchange,提问作者user16641848
相关产品推荐
相关产品推荐

